1 /*
2    +----------------------------------------------------------------------+
3    | Zend OPcache                                                         |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1998-2018 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: Andi Gutmans <andi@zend.com>                                |
16    |          Zeev Suraski <zeev@zend.com>                                |
17    |          Stanislav Malyshev <stas@zend.com>                          |
18    |          Dmitry Stogov <dmitry@zend.com>                             |
19    +----------------------------------------------------------------------+
20 */
21 
22 #include "php.h"
23 #include "Optimizer/zend_optimizer.h"
24 #include "Optimizer/zend_optimizer_internal.h"
25 #include "zend_API.h"
26 #include "zend_constants.h"
27 #include "zend_execute.h"
28 #include "zend_vm.h"
29 #include "zend_cfg.h"
30 #include "zend_func_info.h"
31 #include "zend_call_graph.h"
32 #include "zend_inference.h"
33 #include "zend_dump.h"
34 
35 #ifndef HAVE_DFA_PASS
36 # define HAVE_DFA_PASS 1
37 #endif
38 
zend_optimizer_zval_dtor_wrapper(zval * zvalue)39 static void zend_optimizer_zval_dtor_wrapper(zval *zvalue)
40 {
41 	zval_dtor(zvalue);
42 }
43 
zend_optimizer_collect_constant(zend_optimizer_ctx * ctx,zval * name,zval * value)44 void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, zval *name, zval* value)
45 {
46 	zval val;
47 
48 	if (!ctx->constants) {
49 		ctx->constants = zend_arena_alloc(&ctx->arena, sizeof(HashTable));
50 		zend_hash_init(ctx->constants, 16, NULL, zend_optimizer_zval_dtor_wrapper, 0);
51 	}
52 	ZVAL_DUP(&val, value);
53 	zend_hash_add(ctx->constants, Z_STR_P(name), &val);
54 }
55 
zend_optimizer_get_collected_constant(HashTable * constants,zval * name,zval * value)56 int zend_optimizer_get_collected_constant(HashTable *constants, zval *name, zval* value)
57 {
58 	zval *val;
59 
60 	if ((val = zend_hash_find(constants, Z_STR_P(name))) != NULL) {
61 		ZVAL_DUP(value, val);
62 		return 1;
63 	}
64 	return 0;
65 }
66 
zend_optimizer_lookup_cv(zend_op_array * op_array,zend_string * name)67 int zend_optimizer_lookup_cv(zend_op_array *op_array, zend_string* name)
68 {
69 	int i = 0;
70 	zend_ulong hash_value = zend_string_hash_val(name);
71 
72 	while (i < op_array->last_var) {
73 		if (op_array->vars[i] == name ||
74 		    (ZSTR_H(op_array->vars[i]) == hash_value &&
75 		     ZSTR_LEN(op_array->vars[i]) == ZSTR_LEN(name) &&
76 		     memcmp(ZSTR_VAL(op_array->vars[i]), ZSTR_VAL(name), ZSTR_LEN(name)) == 0)) {
77 			return (int)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, i);
78 		}
79 		i++;
80 	}
81 	i = op_array->last_var;
82 	op_array->last_var++;
83 	op_array->vars = erealloc(op_array->vars, op_array->last_var * sizeof(zend_string*));
84 	op_array->vars[i] = zend_string_dup(name, 0);
85 
86 	/* all IS_TMP_VAR and IS_VAR variable numbers have to be adjusted */
87 	{
88 		zend_op *opline = op_array->opcodes;
89 		zend_op *end = opline + op_array->last;
90 		while (opline < end) {
91 			if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
92 				opline->op1.var += sizeof(zval);
93 			}
94 			if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
95 				opline->op2.var += sizeof(zval);
96 			}
97 			if (opline->result_type & (IS_TMP_VAR|IS_VAR)) {
98 				opline->result.var += sizeof(zval);
99 			}
100 			opline++;
101 		}
102 	}
103 
104 	return (int)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, i);
105 }
106 
zend_optimizer_add_literal(zend_op_array * op_array,zval * zv)107 int zend_optimizer_add_literal(zend_op_array *op_array, zval *zv)
108 {
109 	int i = op_array->last_literal;
110 	op_array->last_literal++;
111 	op_array->literals = (zval*)erealloc(op_array->literals, op_array->last_literal * sizeof(zval));
112 	ZVAL_COPY_VALUE(&op_array->literals[i], zv);
113 	Z_CACHE_SLOT(op_array->literals[i]) = -1;
114 	return i;
115 }
116 
zend_optimizer_add_literal_string(zend_op_array * op_array,zend_string * str)117 static inline int zend_optimizer_add_literal_string(zend_op_array *op_array, zend_string *str) {
118 	zval zv;
119 	ZVAL_STR(&zv, str);
120 	zend_string_hash_val(str);
121 	return zend_optimizer_add_literal(op_array, &zv);
122 }
123 
zend_optimizer_is_disabled_func(const char * name,size_t len)124 int zend_optimizer_is_disabled_func(const char *name, size_t len) {
125 	zend_function *fbc = (zend_function *)zend_hash_str_find_ptr(EG(function_table), name, len);
126 
127 	return (fbc && fbc->type == ZEND_INTERNAL_FUNCTION &&
128 			fbc->internal_function.handler == ZEND_FN(display_disabled_function));
129 }
130 
drop_leading_backslash(zval * val)131 static inline void drop_leading_backslash(zval *val) {
132 	if (Z_STRVAL_P(val)[0] == '\\') {
133 		zend_string *str = zend_string_init(Z_STRVAL_P(val) + 1, Z_STRLEN_P(val) - 1, 0);
134 		zval_dtor(val);
135 		ZVAL_STR(val, str);
136 	}
137 }
138 
alloc_cache_slots_op1(zend_op_array * op_array,zend_op * opline,uint32_t num)139 static inline void alloc_cache_slots_op1(zend_op_array *op_array, zend_op *opline, uint32_t num) {
140 	Z_CACHE_SLOT(op_array->literals[opline->op1.constant]) = op_array->cache_size;
141 	op_array->cache_size += num * sizeof(void *);
142 }
alloc_cache_slots_op2(zend_op_array * op_array,zend_op * opline,uint32_t num)143 static inline void alloc_cache_slots_op2(zend_op_array *op_array, zend_op *opline, uint32_t num) {
144 	Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) = op_array->cache_size;
145 	op_array->cache_size += num * sizeof(void *);
146 }
147 
148 #define REQUIRES_STRING(val) do { \
149 	if (Z_TYPE_P(val) != IS_STRING) { \
150 		zval_dtor(val); \
151 		return 0; \
152 	} \
153 } while (0)
154 
155 #define TO_STRING_NOWARN(val) do { \
156 	if (Z_TYPE_P(val) >= IS_ARRAY) { \
157 		zval_dtor(val); \
158 		return 0; \
159 	} \
160 	convert_to_string(val); \
161 } while (0)
162 
zend_optimizer_update_op1_const(zend_op_array * op_array,zend_op * opline,zval * val)163 int zend_optimizer_update_op1_const(zend_op_array *op_array,
164                                     zend_op       *opline,
165                                     zval          *val)
166 {
167 	switch (opline->opcode) {
168 		case ZEND_FREE:
169 			MAKE_NOP(opline);
170 			zval_dtor(val);
171 			return 1;
172 		case ZEND_INIT_STATIC_METHOD_CALL:
173 		case ZEND_CATCH:
174 		case ZEND_FETCH_CONSTANT:
175 		case ZEND_FETCH_CLASS_CONSTANT:
176 		case ZEND_DEFINED:
177 		case ZEND_NEW:
178 			REQUIRES_STRING(val);
179 			drop_leading_backslash(val);
180 			opline->op1.constant = zend_optimizer_add_literal(op_array, val);
181 			alloc_cache_slots_op1(op_array, opline, 1);
182 			zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
183 			break;
184 		case ZEND_FETCH_STATIC_PROP_R:
185 		case ZEND_FETCH_STATIC_PROP_W:
186 		case ZEND_FETCH_STATIC_PROP_RW:
187 		case ZEND_FETCH_STATIC_PROP_IS:
188 		case ZEND_FETCH_STATIC_PROP_UNSET:
189 		case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
190 			TO_STRING_NOWARN(val);
191 			opline->op1.constant = zend_optimizer_add_literal(op_array, val);
192 			alloc_cache_slots_op1(op_array, opline, 2);
193 			break;
194 		case ZEND_SEND_VAR:
195 			opline->opcode = ZEND_SEND_VAL;
196 			opline->op1.constant = zend_optimizer_add_literal(op_array, val);
197 			break;
198 		case ZEND_SEPARATE:
199 		case ZEND_SEND_VAR_NO_REF:
200 		case ZEND_SEND_VAR_NO_REF_EX:
201 			zval_ptr_dtor(val);
202 			return 0;
203 		case ZEND_VERIFY_RETURN_TYPE:
204 			/* This would require a non-local change.
205 			 * zend_optimizer_replace_by_const() supports this. */
206 			zval_ptr_dtor(val);
207 			return 0;
208 		case ZEND_CONCAT:
209 		case ZEND_FAST_CONCAT:
210 		case ZEND_FETCH_R:
211 		case ZEND_FETCH_W:
212 		case ZEND_FETCH_RW:
213 		case ZEND_FETCH_IS:
214 		case ZEND_FETCH_UNSET:
215 		case ZEND_FETCH_FUNC_ARG:
216 			TO_STRING_NOWARN(val);
217 			/* break missing intentionally */
218 		default:
219 			opline->op1.constant = zend_optimizer_add_literal(op_array, val);
220 			break;
221 	}
222 
223 	ZEND_OP1_TYPE(opline) = IS_CONST;
224 	if (Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) {
225 		zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline)));
226 	}
227 	return 1;
228 }
229 
zend_optimizer_update_op2_const(zend_op_array * op_array,zend_op * opline,zval * val)230 int zend_optimizer_update_op2_const(zend_op_array *op_array,
231                                     zend_op       *opline,
232                                     zval          *val)
233 {
234 	switch (opline->opcode) {
235 		case ZEND_ASSIGN_REF:
236 		case ZEND_FAST_CALL:
237 			zval_dtor(val);
238 			return 0;
239 		case ZEND_FETCH_CLASS:
240 		case ZEND_INIT_FCALL_BY_NAME:
241 		/*case ZEND_INIT_NS_FCALL_BY_NAME:*/
242 		case ZEND_ADD_INTERFACE:
243 		case ZEND_ADD_TRAIT:
244 		case ZEND_INSTANCEOF:
245 		case ZEND_FETCH_STATIC_PROP_R:
246 		case ZEND_FETCH_STATIC_PROP_W:
247 		case ZEND_FETCH_STATIC_PROP_RW:
248 		case ZEND_FETCH_STATIC_PROP_IS:
249 		case ZEND_FETCH_STATIC_PROP_UNSET:
250 		case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
251 		case ZEND_UNSET_STATIC_PROP:
252 		case ZEND_ISSET_ISEMPTY_STATIC_PROP:
253 			REQUIRES_STRING(val);
254 			drop_leading_backslash(val);
255 			opline->op2.constant = zend_optimizer_add_literal(op_array, val);
256 			zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
257 			alloc_cache_slots_op2(op_array, opline, 1);
258 			break;
259 		case ZEND_INIT_FCALL:
260 			REQUIRES_STRING(val);
261 			zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val));
262 			opline->op2.constant = zend_optimizer_add_literal(op_array, val);
263 			alloc_cache_slots_op2(op_array, opline, 1);
264 			break;
265 		case ZEND_INIT_DYNAMIC_CALL:
266 			if (Z_TYPE_P(val) == IS_STRING) {
267 				if (zend_memrchr(Z_STRVAL_P(val), ':', Z_STRLEN_P(val))) {
268 					zval_dtor(val);
269 					return 0;
270 				}
271 
272 				if (zend_optimizer_classify_function(Z_STR_P(val), opline->extended_value)) {
273 					/* Dynamic call to various special functions must stay dynamic,
274 					 * otherwise would drop a warning */
275 					zval_dtor(val);
276 					return 0;
277 				}
278 
279 				opline->opcode = ZEND_INIT_FCALL_BY_NAME;
280 				drop_leading_backslash(val);
281 				opline->op2.constant = zend_optimizer_add_literal(op_array, val);
282 				zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
283 				alloc_cache_slots_op2(op_array, opline, 1);
284 			} else {
285 				opline->op2.constant = zend_optimizer_add_literal(op_array, val);
286 			}
287 			break;
288 		case ZEND_INIT_METHOD_CALL:
289 		case ZEND_INIT_STATIC_METHOD_CALL:
290 			REQUIRES_STRING(val);
291 			opline->op2.constant = zend_optimizer_add_literal(op_array, val);
292 			zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val)));
293 			alloc_cache_slots_op2(op_array, opline, 2);
294 			break;
295 		/*case ZEND_FETCH_CLASS_CONSTANT:*/
296 		case ZEND_ASSIGN_OBJ:
297 		case ZEND_FETCH_OBJ_R:
298 		case ZEND_FETCH_OBJ_W:
299 		case ZEND_FETCH_OBJ_RW:
300 		case ZEND_FETCH_OBJ_IS:
301 		case ZEND_FETCH_OBJ_UNSET:
302 		case ZEND_FETCH_OBJ_FUNC_ARG:
303 		case ZEND_UNSET_OBJ:
304 		case ZEND_PRE_INC_OBJ:
305 		case ZEND_PRE_DEC_OBJ:
306 		case ZEND_POST_INC_OBJ:
307 		case ZEND_POST_DEC_OBJ:
308 		case ZEND_ISSET_ISEMPTY_PROP_OBJ:
309 			TO_STRING_NOWARN(val);
310 			opline->op2.constant = zend_optimizer_add_literal(op_array, val);
311 			alloc_cache_slots_op2(op_array, opline, 2);
312 			break;
313 		case ZEND_ASSIGN_ADD:
314 		case ZEND_ASSIGN_SUB:
315 		case ZEND_ASSIGN_MUL:
316 		case ZEND_ASSIGN_DIV:
317 		case ZEND_ASSIGN_POW:
318 		case ZEND_ASSIGN_MOD:
319 		case ZEND_ASSIGN_SL:
320 		case ZEND_ASSIGN_SR:
321 		case ZEND_ASSIGN_CONCAT:
322 		case ZEND_ASSIGN_BW_OR:
323 		case ZEND_ASSIGN_BW_AND:
324 		case ZEND_ASSIGN_BW_XOR:
325 			if (opline->extended_value == ZEND_ASSIGN_OBJ) {
326 				TO_STRING_NOWARN(val);
327 				opline->op2.constant = zend_optimizer_add_literal(op_array, val);
328 				alloc_cache_slots_op2(op_array, opline, 2);
329 			} else {
330 				opline->op2.constant = zend_optimizer_add_literal(op_array, val);
331 			}
332 			break;
333 		case ZEND_ISSET_ISEMPTY_DIM_OBJ:
334 		case ZEND_ADD_ARRAY_ELEMENT:
335 		case ZEND_INIT_ARRAY:
336 		case ZEND_ASSIGN_DIM:
337 		case ZEND_UNSET_DIM:
338 		case ZEND_FETCH_DIM_R:
339 		case ZEND_FETCH_DIM_W:
340 		case ZEND_FETCH_DIM_RW:
341 		case ZEND_FETCH_DIM_IS:
342 		case ZEND_FETCH_DIM_FUNC_ARG:
343 		case ZEND_FETCH_DIM_UNSET:
344 		case ZEND_FETCH_LIST:
345 			if (Z_TYPE_P(val) == IS_STRING) {
346 				zend_ulong index;
347 				if (ZEND_HANDLE_NUMERIC(Z_STR_P(val), index)) {
348 					zval_dtor(val);
349 					ZVAL_LONG(val, index);
350 				}
351 			}
352 			opline->op2.constant = zend_optimizer_add_literal(op_array, val);
353 			break;
354 		case ZEND_ROPE_INIT:
355 		case ZEND_ROPE_ADD:
356 		case ZEND_ROPE_END:
357 		case ZEND_CONCAT:
358 		case ZEND_FAST_CONCAT:
359 			TO_STRING_NOWARN(val);
360 			/* break missing intentionally */
361 		default:
362 			opline->op2.constant = zend_optimizer_add_literal(op_array, val);
363 			break;
364 	}
365 
366 	ZEND_OP2_TYPE(opline) = IS_CONST;
367 	if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
368 		zend_string_hash_val(Z_STR(ZEND_OP2_LITERAL(opline)));
369 	}
370 	return 1;
371 }
372 
zend_optimizer_remove_live_range(zend_op_array * op_array,uint32_t var)373 void zend_optimizer_remove_live_range(zend_op_array *op_array, uint32_t var)
374 {
375 	if (op_array->last_live_range) {
376 		int i = 0;
377 		int j = 0;
378 		uint32_t *map;
379 		ALLOCA_FLAG(use_heap);
380 
381 		map = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last_live_range, use_heap);
382 
383 		do {
384 			if ((op_array->live_range[i].var & ~ZEND_LIVE_MASK) != var) {
385 				map[i] = j;
386 				if (i != j) {
387 					op_array->live_range[j] = op_array->live_range[i];
388 				}
389 				j++;
390 			}
391 			i++;
392 		} while (i < op_array->last_live_range);
393 		if (i != j) {
394 			if ((op_array->last_live_range = j)) {
395 				zend_op *opline = op_array->opcodes;
396 				zend_op *end = opline + op_array->last;
397 
398 				while (opline != end) {
399 					if ((opline->opcode == ZEND_FREE || opline->opcode == ZEND_FE_FREE) &&
400 							opline->extended_value == ZEND_FREE_ON_RETURN) {
401 						opline->op2.num = map[opline->op2.num];
402 					}
403 					opline++;
404 				}
405 			} else {
406 				efree(op_array->live_range);
407 				op_array->live_range = NULL;
408 			}
409 		}
410 		free_alloca(map, use_heap);
411 	}
412 }
413 
zend_optimizer_replace_by_const(zend_op_array * op_array,zend_op * opline,zend_uchar type,uint32_t var,zval * val)414 int zend_optimizer_replace_by_const(zend_op_array *op_array,
415                                     zend_op       *opline,
416                                     zend_uchar     type,
417                                     uint32_t       var,
418                                     zval          *val)
419 {
420 	zend_op *end = op_array->opcodes + op_array->last;
421 
422 	while (opline < end) {
423 		if (ZEND_OP1_TYPE(opline) == type &&
424 			ZEND_OP1(opline).var == var) {
425 			switch (opline->opcode) {
426 				case ZEND_FETCH_DIM_W:
427 				case ZEND_FETCH_DIM_RW:
428 				case ZEND_FETCH_DIM_FUNC_ARG:
429 				case ZEND_FETCH_DIM_UNSET:
430 				case ZEND_ASSIGN_DIM:
431 				case ZEND_SEPARATE:
432 				case ZEND_RETURN_BY_REF:
433 					zval_dtor(val);
434 					return 0;
435 				case ZEND_SEND_VAR:
436 					opline->extended_value = 0;
437 					opline->opcode = ZEND_SEND_VAL;
438 					break;
439 				case ZEND_SEND_VAR_EX:
440 					opline->extended_value = 0;
441 					opline->opcode = ZEND_SEND_VAL_EX;
442 					break;
443 				case ZEND_SEND_VAR_NO_REF:
444 					zval_dtor(val);
445 					return 0;
446 				case ZEND_SEND_VAR_NO_REF_EX:
447 					opline->opcode = ZEND_SEND_VAL;
448 					break;
449 				case ZEND_SEND_USER:
450 					opline->opcode = ZEND_SEND_VAL_EX;
451 					break;
452 				/* In most cases IS_TMP_VAR operand may be used only once.
453 				 * The operands are usually destroyed by the opcode handler.
454 				 * ZEND_CASE and ZEND_FETCH_LIST are exceptions, they keeps operand
455 				 * unchanged, and allows its reuse. these instructions
456 				 * usually terminated by ZEND_FREE that finally kills the value.
457 				 */
458 				case ZEND_FETCH_LIST: {
459 					zend_op *m = opline;
460 					do {
461 						if (m->opcode == ZEND_FETCH_LIST &&
462 							ZEND_OP1_TYPE(m) == type &&
463 							ZEND_OP1(m).var == var) {
464 							zend_optimizer_update_op1_const(op_array, m, val);
465 						}
466 						m++;
467 					} while (m->opcode != ZEND_FREE || ZEND_OP1_TYPE(m) != type || ZEND_OP1(m).var != var);
468 					ZEND_ASSERT(m->opcode == ZEND_FREE && ZEND_OP1_TYPE(m) == type && ZEND_OP1(m).var == var);
469 					MAKE_NOP(m);
470 					zend_optimizer_remove_live_range(op_array, var);
471 					return 1;
472 				}
473 				case ZEND_CASE:
474 				case ZEND_FREE: {
475 					zend_op *m, *n;
476 					int brk = op_array->last_live_range;
477 					zend_bool in_switch = 0;
478 					while (brk--) {
479 						if (op_array->live_range[brk].start <= (uint32_t)(opline - op_array->opcodes) &&
480 						    op_array->live_range[brk].end > (uint32_t)(opline - op_array->opcodes)) {
481 							in_switch = 1;
482 							break;
483 						}
484 					}
485 
486 					if (!in_switch) {
487 						ZEND_ASSERT(opline->opcode == ZEND_FREE);
488 						MAKE_NOP(opline);
489 						zval_dtor(val);
490 						return 1;
491 					}
492 
493 					m = opline;
494 					n = op_array->opcodes + op_array->live_range[brk].end;
495 					if (n->opcode == ZEND_FREE &&
496 					    !(n->extended_value & ZEND_FREE_ON_RETURN)) {
497 						n++;
498 					} else {
499 						n = op_array->opcodes + op_array->last;
500 					}
501 					while (m < n) {
502 						if (ZEND_OP1_TYPE(m) == type &&
503 								ZEND_OP1(m).var == var) {
504 							if (m->opcode == ZEND_CASE) {
505 								zval old_val;
506 								ZVAL_COPY_VALUE(&old_val, val);
507 								zval_copy_ctor(val);
508 								zend_optimizer_update_op1_const(op_array, m, val);
509 								ZVAL_COPY_VALUE(val, &old_val);
510 							} else if (m->opcode == ZEND_FREE) {
511 								MAKE_NOP(m);
512 							} else {
513 								ZEND_ASSERT(0);
514 							}
515 						}
516 						m++;
517 					}
518 					zval_dtor(val);
519 					zend_optimizer_remove_live_range(op_array, var);
520 					return 1;
521 				}
522 				case ZEND_VERIFY_RETURN_TYPE: {
523 					zend_arg_info *ret_info = op_array->arg_info - 1;
524 					if (ret_info->class_name
525 						|| ret_info->type_hint == IS_CALLABLE
526 						|| !ZEND_SAME_FAKE_TYPE(ret_info->type_hint, Z_TYPE_P(val))
527 						|| (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
528 						zval_dtor(val);
529 						return 0;
530 					}
531 					MAKE_NOP(opline);
532 
533 					/* zend_handle_loops_and_finally may inserts other oplines */
534 					do {
535 						++opline;
536 					} while (opline->opcode != ZEND_RETURN && opline->opcode != ZEND_RETURN_BY_REF);
537 					ZEND_ASSERT(ZEND_OP1(opline).var == var);
538 
539 					break;
540 				  }
541 				default:
542 					break;
543 			}
544 			if (zend_optimizer_update_op1_const(op_array, opline, val)) {
545 				zend_optimizer_remove_live_range(op_array, var);
546 				return 1;
547 			}
548 			return 0;
549 		}
550 
551 		if (ZEND_OP2_TYPE(opline) == type &&
552 			ZEND_OP2(opline).var == var) {
553 			if (zend_optimizer_update_op2_const(op_array, opline, val)) {
554 				zend_optimizer_remove_live_range(op_array, var);
555 				return 1;
556 			}
557 			return 0;
558 		}
559 		opline++;
560 	}
561 
562 	return 1;
563 }
564 
get_class_entry_from_op1(zend_script * script,zend_op_array * op_array,zend_op * opline,zend_bool rt_constants)565 static zend_class_entry *get_class_entry_from_op1(
566 		zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants) {
567 	if (opline->op1_type == IS_CONST) {
568 		zval *op1 = CRT_CONSTANT_EX(op_array, opline->op1, rt_constants);
569 		if (Z_TYPE_P(op1) == IS_STRING) {
570 			zend_string *class_name = Z_STR_P(op1 + 1);
571 			zend_class_entry *ce;
572 			if (script && (ce = zend_hash_find_ptr(&script->class_table, class_name))) {
573 				return ce;
574 			} else if ((ce = zend_hash_find_ptr(EG(class_table), class_name))) {
575 				if (ce->type == ZEND_INTERNAL_CLASS) {
576 					return ce;
577 				} else if (ce->type == ZEND_USER_CLASS &&
578 						   ce->info.user.filename &&
579 						   ce->info.user.filename == op_array->filename) {
580 					return ce;
581 				}
582 			}
583 		}
584 	} else if (opline->op1_type == IS_UNUSED && op_array->scope
585 			&& !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)
586 			&& (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
587 		return op_array->scope;
588 	}
589 	return NULL;
590 }
591 
zend_optimizer_get_called_func(zend_script * script,zend_op_array * op_array,zend_op * opline,zend_bool rt_constants)592 zend_function *zend_optimizer_get_called_func(
593 		zend_script *script, zend_op_array *op_array, zend_op *opline, zend_bool rt_constants)
594 {
595 #define GET_OP(op) CRT_CONSTANT_EX(op_array, opline->op, rt_constants)
596 	switch (opline->opcode) {
597 		case ZEND_INIT_FCALL:
598 		{
599 			zend_string *function_name = Z_STR_P(GET_OP(op2));
600 			zend_function *func;
601 			if (script && (func = zend_hash_find_ptr(&script->function_table, function_name)) != NULL) {
602 				return func;
603 			} else if ((func = zend_hash_find_ptr(EG(function_table), function_name)) != NULL) {
604 				if (func->type == ZEND_INTERNAL_FUNCTION) {
605 					return func;
606 				} else if (func->type == ZEND_USER_FUNCTION &&
607 				           func->op_array.filename &&
608 				           func->op_array.filename == op_array->filename) {
609 					return func;
610 				}
611 			}
612 			break;
613 		}
614 		case ZEND_INIT_FCALL_BY_NAME:
615 		case ZEND_INIT_NS_FCALL_BY_NAME:
616 			if (opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING) {
617 				zval *function_name = GET_OP(op2) + 1;
618 				zend_function *func;
619 				if (script && (func = zend_hash_find_ptr(&script->function_table, Z_STR_P(function_name)))) {
620 					return func;
621 				} else if ((func = zend_hash_find_ptr(EG(function_table), Z_STR_P(function_name))) != NULL) {
622 					if (func->type == ZEND_INTERNAL_FUNCTION) {
623 						return func;
624 					} else if (func->type == ZEND_USER_FUNCTION &&
625 					           func->op_array.filename &&
626 					           func->op_array.filename == op_array->filename) {
627 						return func;
628 					}
629 				}
630 			}
631 			break;
632 		case ZEND_INIT_STATIC_METHOD_CALL:
633 			if (opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING) {
634 				zend_class_entry *ce = get_class_entry_from_op1(
635 					script, op_array, opline, rt_constants);
636 				if (ce) {
637 					zend_string *func_name = Z_STR_P(GET_OP(op2) + 1);
638 					return zend_hash_find_ptr(&ce->function_table, func_name);
639 				}
640 			}
641 			break;
642 		case ZEND_INIT_METHOD_CALL:
643 			if (opline->op1_type == IS_UNUSED
644 					&& opline->op2_type == IS_CONST && Z_TYPE_P(GET_OP(op2)) == IS_STRING
645 					&& op_array->scope && !(op_array->scope->ce_flags & ZEND_ACC_TRAIT)) {
646 				zend_string *method_name = Z_STR_P(GET_OP(op2) + 1);
647 				zend_function *fbc = zend_hash_find_ptr(
648 					&op_array->scope->function_table, method_name);
649 				if (fbc) {
650 					zend_bool is_private = (fbc->common.fn_flags & ZEND_ACC_PRIVATE) != 0;
651 					zend_bool is_final = (fbc->common.fn_flags & ZEND_ACC_FINAL) != 0;
652 					zend_bool same_scope = fbc->common.scope == op_array->scope;
653 					if ((is_private && same_scope)
654 							|| (is_final && (!is_private || same_scope))) {
655 						return fbc;
656 					}
657 				}
658 			}
659 			break;
660 		case ZEND_NEW:
661 		{
662 			zend_class_entry *ce = get_class_entry_from_op1(
663 				script, op_array, opline, rt_constants);
664 			if (ce && ce->type == ZEND_USER_CLASS) {
665 				return ce->constructor;
666 			}
667 			break;
668 		}
669 	}
670 	return NULL;
671 #undef GET_OP
672 }
673 
zend_optimizer_classify_function(zend_string * name,uint32_t num_args)674 uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args) {
675 	if (zend_string_equals_literal(name, "extract")) {
676 		return ZEND_FUNC_INDIRECT_VAR_ACCESS;
677 	} else if (zend_string_equals_literal(name, "compact")) {
678 		return ZEND_FUNC_INDIRECT_VAR_ACCESS;
679 	} else if (zend_string_equals_literal(name, "parse_str") && num_args <= 1) {
680 		return ZEND_FUNC_INDIRECT_VAR_ACCESS;
681 	} else if (zend_string_equals_literal(name, "mb_parse_str") && num_args <= 1) {
682 		return ZEND_FUNC_INDIRECT_VAR_ACCESS;
683 	} else if (zend_string_equals_literal(name, "get_defined_vars")) {
684 		return ZEND_FUNC_INDIRECT_VAR_ACCESS;
685 	} else if (zend_string_equals_literal(name, "assert")) {
686 		return ZEND_FUNC_INDIRECT_VAR_ACCESS;
687 	} else if (zend_string_equals_literal(name, "func_num_args")) {
688 		return ZEND_FUNC_VARARG;
689 	} else if (zend_string_equals_literal(name, "func_get_arg")) {
690 		return ZEND_FUNC_VARARG;
691 	} else if (zend_string_equals_literal(name, "func_get_args")) {
692 		return ZEND_FUNC_VARARG;
693 	} else {
694 		return 0;
695 	}
696 }
697 
zend_optimize(zend_op_array * op_array,zend_optimizer_ctx * ctx)698 static void zend_optimize(zend_op_array      *op_array,
699                           zend_optimizer_ctx *ctx)
700 {
701 	if (op_array->type == ZEND_EVAL_CODE) {
702 		return;
703 	}
704 
705 	if (ctx->debug_level & ZEND_DUMP_BEFORE_OPTIMIZER) {
706 		zend_dump_op_array(op_array, 0, "before optimizer", NULL);
707 	}
708 
709 	/* pass 1
710 	 * - substitute persistent constants (true, false, null, etc)
711 	 * - perform compile-time evaluation of constant binary and unary operations
712 	 * - optimize series of ADD_STRING and/or ADD_CHAR
713 	 * - convert CAST(IS_BOOL,x) into BOOL(x)
714          * - pre-evaluate constant function calls
715 	 */
716 	if (ZEND_OPTIMIZER_PASS_1 & ctx->optimization_level) {
717 		zend_optimizer_pass1(op_array, ctx);
718 		if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_1) {
719 			zend_dump_op_array(op_array, 0, "after pass 1", NULL);
720 		}
721 	}
722 
723 	/* pass 2:
724 	 * - convert non-numeric constants to numeric constants in numeric operators
725 	 * - optimize constant conditional JMPs
726 	 */
727 	if (ZEND_OPTIMIZER_PASS_2 & ctx->optimization_level) {
728 		zend_optimizer_pass2(op_array);
729 		if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_2) {
730 			zend_dump_op_array(op_array, 0, "after pass 2", NULL);
731 		}
732 	}
733 
734 	/* pass 3:
735 	 * - optimize $i = $i+expr to $i+=expr
736 	 * - optimize series of JMPs
737 	 * - change $i++ to ++$i where possible
738 	 */
739 	if (ZEND_OPTIMIZER_PASS_3 & ctx->optimization_level) {
740 		zend_optimizer_pass3(op_array);
741 		if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_3) {
742 			zend_dump_op_array(op_array, 0, "after pass 3", NULL);
743 		}
744 	}
745 
746 	/* pass 4:
747 	 * - INIT_FCALL_BY_NAME -> DO_FCALL
748 	 */
749 	if (ZEND_OPTIMIZER_PASS_4 & ctx->optimization_level) {
750 		zend_optimize_func_calls(op_array, ctx);
751 		if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_4) {
752 			zend_dump_op_array(op_array, 0, "after pass 4", NULL);
753 		}
754 	}
755 
756 	/* pass 5:
757 	 * - CFG optimization
758 	 */
759 	if (ZEND_OPTIMIZER_PASS_5 & ctx->optimization_level) {
760 		zend_optimize_cfg(op_array, ctx);
761 		if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_5) {
762 			zend_dump_op_array(op_array, 0, "after pass 5", NULL);
763 		}
764 	}
765 
766 #if HAVE_DFA_PASS
767 	/* pass 6:
768 	 * - DFA optimization
769 	 */
770 	if ((ZEND_OPTIMIZER_PASS_6 & ctx->optimization_level) &&
771 	    !(ZEND_OPTIMIZER_PASS_7 & ctx->optimization_level)) {
772 		zend_optimize_dfa(op_array, ctx);
773 		if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_6) {
774 			zend_dump_op_array(op_array, 0, "after pass 6", NULL);
775 		}
776 	}
777 #endif
778 
779 	/* pass 9:
780 	 * - Optimize temp variables usage
781 	 */
782 	if (ZEND_OPTIMIZER_PASS_9 & ctx->optimization_level) {
783 		zend_optimize_temporary_variables(op_array, ctx);
784 		if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_9) {
785 			zend_dump_op_array(op_array, 0, "after pass 9", NULL);
786 		}
787 	}
788 
789 	/* pass 10:
790 	 * - remove NOPs
791 	 */
792 	if (((ZEND_OPTIMIZER_PASS_10|ZEND_OPTIMIZER_PASS_5) & ctx->optimization_level) == ZEND_OPTIMIZER_PASS_10) {
793 		zend_optimizer_nop_removal(op_array);
794 		if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_10) {
795 			zend_dump_op_array(op_array, 0, "after pass 10", NULL);
796 		}
797 	}
798 
799 	/* pass 11:
800 	 * - Compact literals table
801 	 */
802 	if (ZEND_OPTIMIZER_PASS_11 & ctx->optimization_level) {
803 		zend_optimizer_compact_literals(op_array, ctx);
804 		if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_11) {
805 			zend_dump_op_array(op_array, 0, "after pass 11", NULL);
806 		}
807 	}
808 
809 	if (ctx->debug_level & ZEND_DUMP_AFTER_OPTIMIZER) {
810 		zend_dump_op_array(op_array, 0, "after optimizer", NULL);
811 	}
812 }
813 
zend_revert_pass_two(zend_op_array * op_array)814 static void zend_revert_pass_two(zend_op_array *op_array)
815 {
816 	zend_op *opline, *end;
817 
818 	opline = op_array->opcodes;
819 	end = opline + op_array->last;
820 	while (opline < end) {
821 		if (opline->op1_type == IS_CONST) {
822 			ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline->op1);
823 		}
824 		if (opline->op2_type == IS_CONST) {
825 			ZEND_PASS_TWO_UNDO_CONSTANT(op_array, opline->op2);
826 		}
827 		opline++;
828 	}
829 }
830 
zend_redo_pass_two(zend_op_array * op_array)831 static void zend_redo_pass_two(zend_op_array *op_array)
832 {
833 	zend_op *opline, *end;
834 
835 	opline = op_array->opcodes;
836 	end = opline + op_array->last;
837 	while (opline < end) {
838 		if (opline->op1_type == IS_CONST) {
839 			ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op1);
840 		}
841 		if (opline->op2_type == IS_CONST) {
842 			ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2);
843 		}
844 		ZEND_VM_SET_OPCODE_HANDLER(opline);
845 		opline++;
846 	}
847 }
848 
849 #if HAVE_DFA_PASS
zend_redo_pass_two_ex(zend_op_array * op_array,zend_ssa * ssa)850 static void zend_redo_pass_two_ex(zend_op_array *op_array, zend_ssa *ssa)
851 {
852 	zend_op *opline, *end;
853 
854 	opline = op_array->opcodes;
855 	end = opline + op_array->last;
856 	while (opline < end) {
857 		zend_vm_set_opcode_handler_ex(opline,
858 			opline->op1_type == IS_UNUSED ? 0 : (OP1_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)),
859 			opline->op2_type == IS_UNUSED ? 0 : (OP2_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)),
860 			(opline->opcode == ZEND_PRE_INC ||
861 			 opline->opcode == ZEND_PRE_DEC ||
862 			 opline->opcode == ZEND_POST_INC ||
863 			 opline->opcode == ZEND_POST_DEC) ?
864 				((ssa->ops[opline - op_array->opcodes].op1_def >= 0) ? (OP1_DEF_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY)) : MAY_BE_ANY) :
865 				(opline->result_type == IS_UNUSED ? 0 : (RES_INFO() & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_KEY_ANY))));
866 		if (opline->op1_type == IS_CONST) {
867 			ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op1);
868 		}
869 		if (opline->op2_type == IS_CONST) {
870 			ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline->op2);
871 		}
872 		opline++;
873 	}
874 }
875 #endif
876 
zend_optimize_op_array(zend_op_array * op_array,zend_optimizer_ctx * ctx)877 static void zend_optimize_op_array(zend_op_array      *op_array,
878                                    zend_optimizer_ctx *ctx)
879 {
880 	/* Revert pass_two() */
881 	zend_revert_pass_two(op_array);
882 
883 	/* Do actual optimizations */
884 	zend_optimize(op_array, ctx);
885 
886 	/* Redo pass_two() */
887 	zend_redo_pass_two(op_array);
888 }
889 
zend_adjust_fcall_stack_size(zend_op_array * op_array,zend_optimizer_ctx * ctx)890 static void zend_adjust_fcall_stack_size(zend_op_array *op_array, zend_optimizer_ctx *ctx)
891 {
892 	zend_function *func;
893 	zend_op *opline, *end;
894 
895 	opline = op_array->opcodes;
896 	end = opline + op_array->last;
897 	while (opline < end) {
898 		if (opline->opcode == ZEND_INIT_FCALL) {
899 			func = zend_hash_find_ptr(
900 				&ctx->script->function_table,
901 				Z_STR_P(RT_CONSTANT(op_array, opline->op2)));
902 			if (func) {
903 				opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, func);
904 			}
905 		}
906 		opline++;
907 	}
908 }
909 
910 #if HAVE_DFA_PASS
zend_adjust_fcall_stack_size_graph(zend_op_array * op_array)911 static void zend_adjust_fcall_stack_size_graph(zend_op_array *op_array)
912 {
913 	zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
914 
915 	if (func_info) {
916 		zend_call_info *call_info =func_info->callee_info;
917 
918 		while (call_info) {
919 			zend_op *opline = call_info->caller_init_opline;
920 
921 			if (opline && call_info->callee_func && opline->opcode == ZEND_INIT_FCALL) {
922 				opline->op1.num = zend_vm_calc_used_stack(opline->extended_value, call_info->callee_func);
923 			}
924 			call_info = call_info->next_callee;
925 		}
926 	}
927 }
928 #endif
929 
zend_optimize_script(zend_script * script,zend_long optimization_level,zend_long debug_level)930 int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level)
931 {
932 	zend_class_entry *ce;
933 	zend_op_array *op_array;
934 	zend_string *name;
935 	zend_optimizer_ctx ctx;
936 #if HAVE_DFA_PASS
937 	zend_call_graph call_graph;
938 #endif
939 
940 	ctx.arena = zend_arena_create(64 * 1024);
941 	ctx.script = script;
942 	ctx.constants = NULL;
943 	ctx.optimization_level = optimization_level;
944 	ctx.debug_level = debug_level;
945 
946 	zend_optimize_op_array(&script->main_op_array, &ctx);
947 
948 	ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
949 		zend_optimize_op_array(op_array, &ctx);
950 	} ZEND_HASH_FOREACH_END();
951 
952 	ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
953 		ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
954 			if (op_array->scope == ce) {
955 				zend_optimize_op_array(op_array, &ctx);
956 			} else if (op_array->type == ZEND_USER_FUNCTION) {
957 				zend_op_array *orig_op_array;
958 				if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) {
959 					HashTable *ht = op_array->static_variables;
960 					*op_array = *orig_op_array;
961 					op_array->static_variables = ht;
962 				}
963 			}
964 		} ZEND_HASH_FOREACH_END();
965 	} ZEND_HASH_FOREACH_END();
966 
967 #if HAVE_DFA_PASS
968 	if ((ZEND_OPTIMIZER_PASS_6 & optimization_level) &&
969 	    (ZEND_OPTIMIZER_PASS_7 & optimization_level) &&
970 	    zend_build_call_graph(&ctx.arena, script, ZEND_RT_CONSTANTS, &call_graph) == SUCCESS) {
971 		/* Optimize using call-graph */
972 		void *checkpoint = zend_arena_checkpoint(ctx.arena);
973 		int i;
974 		zend_func_info *func_info;
975 
976 		for (i = 0; i < call_graph.op_arrays_count; i++) {
977 			zend_revert_pass_two(call_graph.op_arrays[i]);
978 		}
979 
980 		for (i = 0; i < call_graph.op_arrays_count; i++) {
981 			func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
982 			if (func_info) {
983 				func_info->call_map = zend_build_call_map(&ctx.arena, func_info, call_graph.op_arrays[i]);
984 				if (call_graph.op_arrays[i]->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
985 					zend_init_func_return_info(call_graph.op_arrays[i], script, &func_info->return_info);
986 				}
987 			}
988 		}
989 
990 		for (i = 0; i < call_graph.op_arrays_count; i++) {
991 			func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
992 			if (func_info) {
993 				zend_dfa_analyze_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa, &func_info->flags);
994 			}
995 		}
996 
997 		//TODO: perform inner-script inference???
998 		for (i = 0; i < call_graph.op_arrays_count; i++) {
999 			func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
1000 			if (func_info) {
1001 				zend_dfa_optimize_op_array(call_graph.op_arrays[i], &ctx, &func_info->ssa);
1002 			}
1003 		}
1004 
1005 		if (debug_level & ZEND_DUMP_AFTER_PASS_7) {
1006 			for (i = 0; i < call_graph.op_arrays_count; i++) {
1007 				zend_dump_op_array(call_graph.op_arrays[i], 0, "after pass 7", NULL);
1008 			}
1009 		}
1010 
1011 		if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
1012 			for (i = 0; i < call_graph.op_arrays_count; i++) {
1013 				zend_adjust_fcall_stack_size_graph(call_graph.op_arrays[i]);
1014 			}
1015 		}
1016 
1017 		for (i = 0; i < call_graph.op_arrays_count; i++) {
1018 			func_info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
1019 			if (func_info && func_info->ssa.var_info) {
1020 				zend_redo_pass_two_ex(call_graph.op_arrays[i], &func_info->ssa);
1021 			} else {
1022 				zend_redo_pass_two(call_graph.op_arrays[i]);
1023 			}
1024 		}
1025 
1026 		for (i = 0; i < call_graph.op_arrays_count; i++) {
1027 			ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
1028 		}
1029 
1030 		ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
1031 			ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
1032 				if (op_array->scope != ce) {
1033 					zend_op_array *orig_op_array;
1034 					if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) {
1035 						HashTable *ht = op_array->static_variables;
1036 						*op_array = *orig_op_array;
1037 						op_array->static_variables = ht;
1038 					}
1039 				}
1040 			} ZEND_HASH_FOREACH_END();
1041 		} ZEND_HASH_FOREACH_END();
1042 
1043 		zend_arena_release(&ctx.arena, checkpoint);
1044 	} else
1045 #endif
1046 
1047 	if (ZEND_OPTIMIZER_PASS_12 & optimization_level) {
1048 		zend_adjust_fcall_stack_size(&script->main_op_array, &ctx);
1049 
1050 		ZEND_HASH_FOREACH_PTR(&script->function_table, op_array) {
1051 			zend_adjust_fcall_stack_size(op_array, &ctx);
1052 		} ZEND_HASH_FOREACH_END();
1053 
1054 		ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
1055 			ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
1056 				if (op_array->scope == ce) {
1057 					zend_adjust_fcall_stack_size(op_array, &ctx);
1058 				} else if (op_array->type == ZEND_USER_FUNCTION) {
1059 					zend_op_array *orig_op_array;
1060 					if ((orig_op_array = zend_hash_find_ptr(&op_array->scope->function_table, name)) != NULL) {
1061 						HashTable *ht = op_array->static_variables;
1062 						*op_array = *orig_op_array;
1063 						op_array->static_variables = ht;
1064 					}
1065 				}
1066 			} ZEND_HASH_FOREACH_END();
1067 		} ZEND_HASH_FOREACH_END();
1068 	}
1069 
1070 	if (ctx.constants) {
1071 		zend_hash_destroy(ctx.constants);
1072 	}
1073 	zend_arena_destroy(ctx.arena);
1074 
1075 	return 1;
1076 }
1077 
zend_optimizer_startup(void)1078 int zend_optimizer_startup(void)
1079 {
1080 	return zend_func_info_startup();
1081 }
1082 
zend_optimizer_shutdown(void)1083 int zend_optimizer_shutdown(void)
1084 {
1085 	return zend_func_info_shutdown();
1086 }
1087 
1088 /*
1089  * Local variables:
1090  * tab-width: 4
1091  * c-basic-offset: 4
1092  * indent-tabs-mode: t
1093  * End:
1094  */
1095