1 /*
2 +----------------------------------------------------------------------+
3 | Zend Engine |
4 +----------------------------------------------------------------------+
5 | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 2.00 of the Zend 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.zend.com/license/2_00.txt. |
11 | If you did not receive a copy of the Zend license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@zend.com so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Andi Gutmans <andi@php.net> |
16 | Zeev Suraski <zeev@php.net> |
17 | Dmitry Stogov <dmitry@php.net> |
18 +----------------------------------------------------------------------+
19 */
20
21 #include <stdio.h>
22
23 #include "zend.h"
24 #include "zend_alloc.h"
25 #include "zend_compile.h"
26 #include "zend_extensions.h"
27 #include "zend_API.h"
28 #include "zend_sort.h"
29 #include "zend_constants.h"
30 #include "zend_observer.h"
31
32 #include "zend_vm.h"
33
zend_extension_op_array_ctor_handler(zend_extension * extension,zend_op_array * op_array)34 static void zend_extension_op_array_ctor_handler(zend_extension *extension, zend_op_array *op_array)
35 {
36 if (extension->op_array_ctor) {
37 extension->op_array_ctor(op_array);
38 }
39 }
40
zend_extension_op_array_dtor_handler(zend_extension * extension,zend_op_array * op_array)41 static void zend_extension_op_array_dtor_handler(zend_extension *extension, zend_op_array *op_array)
42 {
43 if (extension->op_array_dtor) {
44 extension->op_array_dtor(op_array);
45 }
46 }
47
init_op_array(zend_op_array * op_array,uint8_t type,int initial_ops_size)48 void init_op_array(zend_op_array *op_array, uint8_t type, int initial_ops_size)
49 {
50 op_array->type = type;
51 op_array->arg_flags[0] = 0;
52 op_array->arg_flags[1] = 0;
53 op_array->arg_flags[2] = 0;
54
55 op_array->refcount = (uint32_t *) emalloc(sizeof(uint32_t));
56 *op_array->refcount = 1;
57 op_array->last = 0;
58 op_array->opcodes = emalloc(initial_ops_size * sizeof(zend_op));
59
60 op_array->last_var = 0;
61 op_array->vars = NULL;
62
63 op_array->T = 0;
64
65 op_array->function_name = NULL;
66 op_array->filename = zend_string_copy(zend_get_compiled_filename());
67 op_array->doc_comment = NULL;
68 op_array->attributes = NULL;
69
70 op_array->arg_info = NULL;
71 op_array->num_args = 0;
72 op_array->required_num_args = 0;
73
74 op_array->scope = NULL;
75 op_array->prototype = NULL;
76
77 op_array->live_range = NULL;
78 op_array->try_catch_array = NULL;
79 op_array->last_live_range = 0;
80
81 op_array->static_variables = NULL;
82 ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, NULL);
83 op_array->last_try_catch = 0;
84
85 op_array->fn_flags = 0;
86
87 op_array->last_literal = 0;
88 op_array->literals = NULL;
89
90 op_array->num_dynamic_func_defs = 0;
91 op_array->dynamic_func_defs = NULL;
92
93 ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL);
94 op_array->cache_size = zend_op_array_extension_handles * sizeof(void*);
95
96 memset(op_array->reserved, 0, ZEND_MAX_RESERVED_RESOURCES * sizeof(void*));
97
98 if (zend_extension_flags & ZEND_EXTENSIONS_HAVE_OP_ARRAY_CTOR) {
99 zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_ctor_handler, op_array);
100 }
101 }
102
destroy_zend_function(zend_function * function)103 ZEND_API void destroy_zend_function(zend_function *function)
104 {
105 zval tmp;
106
107 ZVAL_PTR(&tmp, function);
108 zend_function_dtor(&tmp);
109 }
110
zend_type_release(zend_type type,bool persistent)111 ZEND_API void zend_type_release(zend_type type, bool persistent) {
112 if (ZEND_TYPE_HAS_LIST(type)) {
113 zend_type *list_type;
114 ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) {
115 zend_type_release(*list_type, persistent);
116 } ZEND_TYPE_LIST_FOREACH_END();
117 if (!ZEND_TYPE_USES_ARENA(type)) {
118 pefree(ZEND_TYPE_LIST(type), persistent);
119 }
120 } else if (ZEND_TYPE_HAS_NAME(type)) {
121 zend_string_release(ZEND_TYPE_NAME(type));
122 }
123 }
124
zend_free_internal_arg_info(zend_internal_function * function)125 void zend_free_internal_arg_info(zend_internal_function *function) {
126 if ((function->fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS)) &&
127 function->arg_info) {
128
129 uint32_t i;
130 uint32_t num_args = function->num_args + 1;
131 zend_internal_arg_info *arg_info = function->arg_info - 1;
132
133 if (function->fn_flags & ZEND_ACC_VARIADIC) {
134 num_args++;
135 }
136 for (i = 0 ; i < num_args; i++) {
137 zend_type_release(arg_info[i].type, /* persistent */ 1);
138 }
139 free(arg_info);
140 }
141 }
142
zend_function_dtor(zval * zv)143 ZEND_API void zend_function_dtor(zval *zv)
144 {
145 zend_function *function = Z_PTR_P(zv);
146
147 if (function->type == ZEND_USER_FUNCTION) {
148 ZEND_ASSERT(function->common.function_name);
149 destroy_op_array(&function->op_array);
150 /* op_arrays are allocated on arena, so we don't have to free them */
151 } else {
152 ZEND_ASSERT(function->type == ZEND_INTERNAL_FUNCTION);
153 ZEND_ASSERT(function->common.function_name);
154 zend_string_release_ex(function->common.function_name, 1);
155
156 /* For methods this will be called explicitly. */
157 if (!function->common.scope) {
158 zend_free_internal_arg_info(&function->internal_function);
159
160 if (function->common.attributes) {
161 zend_hash_release(function->common.attributes);
162 function->common.attributes = NULL;
163 }
164 }
165
166 if (!(function->common.fn_flags & ZEND_ACC_ARENA_ALLOCATED)) {
167 pefree(function, 1);
168 }
169 }
170 }
171
zend_cleanup_internal_class_data(zend_class_entry * ce)172 ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce)
173 {
174 if (ZEND_MAP_PTR(ce->static_members_table) && CE_STATIC_MEMBERS(ce)) {
175 zval *static_members = CE_STATIC_MEMBERS(ce);
176 zval *p = static_members;
177 zval *end = p + ce->default_static_members_count;
178 ZEND_MAP_PTR_SET(ce->static_members_table, NULL);
179 while (p != end) {
180 if (UNEXPECTED(Z_ISREF_P(p))) {
181 zend_property_info *prop_info;
182 ZEND_REF_FOREACH_TYPE_SOURCES(Z_REF_P(p), prop_info) {
183 if (prop_info->ce == ce && p - static_members == prop_info->offset) {
184 ZEND_REF_DEL_TYPE_SOURCE(Z_REF_P(p), prop_info);
185 break; /* stop iteration here, the array might be realloc()'ed */
186 }
187 } ZEND_REF_FOREACH_TYPE_SOURCES_END();
188 }
189 i_zval_ptr_dtor(p);
190 p++;
191 }
192 efree(static_members);
193 }
194 }
195
_destroy_zend_class_traits_info(zend_class_entry * ce)196 static void _destroy_zend_class_traits_info(zend_class_entry *ce)
197 {
198 uint32_t i;
199
200 for (i = 0; i < ce->num_traits; i++) {
201 zend_string_release_ex(ce->trait_names[i].name, 0);
202 zend_string_release_ex(ce->trait_names[i].lc_name, 0);
203 }
204 efree(ce->trait_names);
205
206 if (ce->trait_aliases) {
207 i = 0;
208 while (ce->trait_aliases[i]) {
209 if (ce->trait_aliases[i]->trait_method.method_name) {
210 zend_string_release_ex(ce->trait_aliases[i]->trait_method.method_name, 0);
211 }
212 if (ce->trait_aliases[i]->trait_method.class_name) {
213 zend_string_release_ex(ce->trait_aliases[i]->trait_method.class_name, 0);
214 }
215
216 if (ce->trait_aliases[i]->alias) {
217 zend_string_release_ex(ce->trait_aliases[i]->alias, 0);
218 }
219
220 efree(ce->trait_aliases[i]);
221 i++;
222 }
223
224 efree(ce->trait_aliases);
225 }
226
227 if (ce->trait_precedences) {
228 uint32_t j;
229
230 i = 0;
231 while (ce->trait_precedences[i]) {
232 zend_string_release_ex(ce->trait_precedences[i]->trait_method.method_name, 0);
233 zend_string_release_ex(ce->trait_precedences[i]->trait_method.class_name, 0);
234
235 for (j = 0; j < ce->trait_precedences[i]->num_excludes; j++) {
236 zend_string_release_ex(ce->trait_precedences[i]->exclude_class_names[j], 0);
237 }
238 efree(ce->trait_precedences[i]);
239 i++;
240 }
241 efree(ce->trait_precedences);
242 }
243 }
244
zend_cleanup_mutable_class_data(zend_class_entry * ce)245 ZEND_API void zend_cleanup_mutable_class_data(zend_class_entry *ce)
246 {
247 zend_class_mutable_data *mutable_data = ZEND_MAP_PTR_GET_IMM(ce->mutable_data);
248
249 if (mutable_data) {
250 HashTable *constants_table;
251 zval *p;
252
253 constants_table = mutable_data->constants_table;
254 if (constants_table && constants_table != &ce->constants_table) {
255 zend_class_constant *c;
256
257 ZEND_HASH_MAP_FOREACH_PTR(constants_table, c) {
258 if (c->ce == ce || (Z_CONSTANT_FLAGS(c->value) & CONST_OWNED)) {
259 zval_ptr_dtor_nogc(&c->value);
260 }
261 } ZEND_HASH_FOREACH_END();
262 zend_hash_destroy(constants_table);
263 mutable_data->constants_table = NULL;
264 }
265
266 p = mutable_data->default_properties_table;
267 if (p && p != ce->default_properties_table) {
268 zval *end = p + ce->default_properties_count;
269
270 while (p < end) {
271 zval_ptr_dtor_nogc(p);
272 p++;
273 }
274 mutable_data->default_properties_table = NULL;
275 }
276
277 if (mutable_data->backed_enum_table) {
278 zend_hash_release(mutable_data->backed_enum_table);
279 mutable_data->backed_enum_table = NULL;
280 }
281
282 ZEND_MAP_PTR_SET_IMM(ce->mutable_data, NULL);
283 }
284 }
285
destroy_zend_class(zval * zv)286 ZEND_API void destroy_zend_class(zval *zv)
287 {
288 zend_property_info *prop_info;
289 zend_class_entry *ce = Z_PTR_P(zv);
290 zend_function *fn;
291
292 if (ce->ce_flags & ZEND_ACC_IMMUTABLE) {
293 return;
294 }
295
296 /* We don't increase the refcount for class aliases,
297 * skip the destruction of aliases entirely. */
298 if (UNEXPECTED(Z_TYPE_INFO_P(zv) == IS_ALIAS_PTR)) {
299 return;
300 }
301
302 if (ce->ce_flags & ZEND_ACC_FILE_CACHED) {
303 zend_class_constant *c;
304 zval *p, *end;
305
306 ZEND_HASH_MAP_FOREACH_PTR(&ce->constants_table, c) {
307 if (c->ce == ce) {
308 zval_ptr_dtor_nogc(&c->value);
309 }
310 } ZEND_HASH_FOREACH_END();
311
312 if (ce->default_properties_table) {
313 p = ce->default_properties_table;
314 end = p + ce->default_properties_count;
315
316 while (p < end) {
317 zval_ptr_dtor_nogc(p);
318 p++;
319 }
320 }
321 return;
322 }
323
324 ZEND_ASSERT(ce->refcount > 0);
325
326 if (--ce->refcount > 0) {
327 return;
328 }
329
330 switch (ce->type) {
331 case ZEND_USER_CLASS:
332 if (!(ce->ce_flags & ZEND_ACC_CACHED)) {
333 if (ce->parent_name && !(ce->ce_flags & ZEND_ACC_RESOLVED_PARENT)) {
334 zend_string_release_ex(ce->parent_name, 0);
335 }
336
337 zend_string_release_ex(ce->name, 0);
338 zend_string_release_ex(ce->info.user.filename, 0);
339
340 if (ce->info.user.doc_comment) {
341 zend_string_release_ex(ce->info.user.doc_comment, 0);
342 }
343
344 if (ce->attributes) {
345 zend_hash_release(ce->attributes);
346 }
347
348 if (ce->num_interfaces > 0 && !(ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES)) {
349 uint32_t i;
350
351 for (i = 0; i < ce->num_interfaces; i++) {
352 zend_string_release_ex(ce->interface_names[i].name, 0);
353 zend_string_release_ex(ce->interface_names[i].lc_name, 0);
354 }
355 efree(ce->interface_names);
356 }
357
358 if (ce->num_traits > 0) {
359 _destroy_zend_class_traits_info(ce);
360 }
361 }
362
363 if (ce->default_properties_table) {
364 zval *p = ce->default_properties_table;
365 zval *end = p + ce->default_properties_count;
366
367 while (p != end) {
368 i_zval_ptr_dtor(p);
369 p++;
370 }
371 efree(ce->default_properties_table);
372 }
373 if (ce->default_static_members_table) {
374 zval *p = ce->default_static_members_table;
375 zval *end = p + ce->default_static_members_count;
376
377 while (p != end) {
378 ZEND_ASSERT(!Z_ISREF_P(p));
379 i_zval_ptr_dtor(p);
380 p++;
381 }
382 efree(ce->default_static_members_table);
383 }
384 ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, prop_info) {
385 if (prop_info->ce == ce) {
386 zend_string_release_ex(prop_info->name, 0);
387 if (prop_info->doc_comment) {
388 zend_string_release_ex(prop_info->doc_comment, 0);
389 }
390 if (prop_info->attributes) {
391 zend_hash_release(prop_info->attributes);
392 }
393 zend_type_release(prop_info->type, /* persistent */ 0);
394 }
395 } ZEND_HASH_FOREACH_END();
396 zend_hash_destroy(&ce->properties_info);
397 zend_hash_destroy(&ce->function_table);
398 if (zend_hash_num_elements(&ce->constants_table)) {
399 zend_class_constant *c;
400
401 ZEND_HASH_MAP_FOREACH_PTR(&ce->constants_table, c) {
402 if (c->ce == ce || (Z_CONSTANT_FLAGS(c->value) & CONST_OWNED)) {
403 zval_ptr_dtor_nogc(&c->value);
404 if (c->doc_comment) {
405 zend_string_release_ex(c->doc_comment, 0);
406 }
407 if (c->attributes) {
408 zend_hash_release(c->attributes);
409 }
410 }
411 } ZEND_HASH_FOREACH_END();
412 }
413 zend_hash_destroy(&ce->constants_table);
414 if (ce->num_interfaces > 0 && (ce->ce_flags & ZEND_ACC_RESOLVED_INTERFACES)) {
415 efree(ce->interfaces);
416 }
417 if (ce->backed_enum_table) {
418 zend_hash_release(ce->backed_enum_table);
419 }
420 break;
421 case ZEND_INTERNAL_CLASS:
422 if (ce->backed_enum_table) {
423 zend_hash_release(ce->backed_enum_table);
424 }
425 if (ce->default_properties_table) {
426 zval *p = ce->default_properties_table;
427 zval *end = p + ce->default_properties_count;
428
429 while (p != end) {
430 zval_internal_ptr_dtor(p);
431 p++;
432 }
433 free(ce->default_properties_table);
434 }
435 if (ce->default_static_members_table) {
436 zval *p = ce->default_static_members_table;
437 zval *end = p + ce->default_static_members_count;
438
439 while (p != end) {
440 zval_internal_ptr_dtor(p);
441 p++;
442 }
443 free(ce->default_static_members_table);
444 }
445
446 ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, prop_info) {
447 if (prop_info->ce == ce) {
448 zend_string_release(prop_info->name);
449 zend_type_release(prop_info->type, /* persistent */ 1);
450 if (prop_info->attributes) {
451 zend_hash_release(prop_info->attributes);
452 }
453 free(prop_info);
454 }
455 } ZEND_HASH_FOREACH_END();
456 zend_hash_destroy(&ce->properties_info);
457 zend_string_release_ex(ce->name, 1);
458
459 /* TODO: eliminate this loop for classes without functions with arg_info / attributes */
460 ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, fn) {
461 if (fn->common.scope == ce) {
462 if (fn->common.fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS)) {
463 zend_free_internal_arg_info(&fn->internal_function);
464 }
465
466 if (fn->common.attributes) {
467 zend_hash_release(fn->common.attributes);
468 fn->common.attributes = NULL;
469 }
470 }
471 } ZEND_HASH_FOREACH_END();
472
473 zend_hash_destroy(&ce->function_table);
474 if (zend_hash_num_elements(&ce->constants_table)) {
475 zend_class_constant *c;
476
477 ZEND_HASH_MAP_FOREACH_PTR(&ce->constants_table, c) {
478 if (c->ce == ce) {
479 if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
480 /* We marked this as IMMUTABLE, but do need to free it when the
481 * class is destroyed. */
482 ZEND_ASSERT(Z_ASTVAL(c->value)->kind == ZEND_AST_CONST_ENUM_INIT);
483 free(Z_AST(c->value));
484 } else {
485 zval_internal_ptr_dtor(&c->value);
486 }
487 if (c->doc_comment) {
488 zend_string_release_ex(c->doc_comment, 1);
489 }
490 if (c->attributes) {
491 zend_hash_release(c->attributes);
492 }
493 }
494 free(c);
495 } ZEND_HASH_FOREACH_END();
496 zend_hash_destroy(&ce->constants_table);
497 }
498 if (ce->iterator_funcs_ptr) {
499 free(ce->iterator_funcs_ptr);
500 }
501 if (ce->arrayaccess_funcs_ptr) {
502 free(ce->arrayaccess_funcs_ptr);
503 }
504 if (ce->num_interfaces > 0) {
505 free(ce->interfaces);
506 }
507 if (ce->properties_info_table) {
508 free(ce->properties_info_table);
509 }
510 if (ce->attributes) {
511 zend_hash_release(ce->attributes);
512 }
513 free(ce);
514 break;
515 }
516 }
517
zend_class_add_ref(zval * zv)518 void zend_class_add_ref(zval *zv)
519 {
520 zend_class_entry *ce = Z_PTR_P(zv);
521
522 if (Z_TYPE_P(zv) != IS_ALIAS_PTR && !(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
523 ce->refcount++;
524 }
525 }
526
zend_destroy_static_vars(zend_op_array * op_array)527 ZEND_API void zend_destroy_static_vars(zend_op_array *op_array)
528 {
529 if (ZEND_MAP_PTR(op_array->static_variables_ptr)) {
530 HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr);
531 if (ht) {
532 zend_array_destroy(ht);
533 ZEND_MAP_PTR_SET(op_array->static_variables_ptr, NULL);
534 }
535 }
536 }
537
destroy_op_array(zend_op_array * op_array)538 ZEND_API void destroy_op_array(zend_op_array *op_array)
539 {
540 uint32_t i;
541
542 if ((op_array->fn_flags & ZEND_ACC_HEAP_RT_CACHE)
543 && ZEND_MAP_PTR(op_array->run_time_cache)) {
544 efree(ZEND_MAP_PTR(op_array->run_time_cache));
545 }
546
547 if (op_array->function_name) {
548 zend_string_release_ex(op_array->function_name, 0);
549 }
550
551 if (!op_array->refcount || --(*op_array->refcount) > 0) {
552 return;
553 }
554
555 efree_size(op_array->refcount, sizeof(*(op_array->refcount)));
556
557 if (op_array->vars) {
558 i = op_array->last_var;
559 while (i > 0) {
560 i--;
561 zend_string_release_ex(op_array->vars[i], 0);
562 }
563 efree(op_array->vars);
564 }
565
566 if (op_array->literals) {
567 zval *literal = op_array->literals;
568 zval *end = literal + op_array->last_literal;
569 while (literal < end) {
570 zval_ptr_dtor_nogc(literal);
571 literal++;
572 }
573 if (ZEND_USE_ABS_CONST_ADDR
574 || !(op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO)) {
575 efree(op_array->literals);
576 }
577 }
578 efree(op_array->opcodes);
579
580 zend_string_release_ex(op_array->filename, 0);
581 if (op_array->doc_comment) {
582 zend_string_release_ex(op_array->doc_comment, 0);
583 }
584 if (op_array->attributes) {
585 zend_hash_release(op_array->attributes);
586 }
587 if (op_array->live_range) {
588 efree(op_array->live_range);
589 }
590 if (op_array->try_catch_array) {
591 efree(op_array->try_catch_array);
592 }
593 if (zend_extension_flags & ZEND_EXTENSIONS_HAVE_OP_ARRAY_DTOR) {
594 if (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO) {
595 zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_dtor_handler, op_array);
596 }
597 }
598 if (op_array->arg_info) {
599 uint32_t num_args = op_array->num_args;
600 zend_arg_info *arg_info = op_array->arg_info;
601
602 if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
603 arg_info--;
604 num_args++;
605 }
606 if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
607 num_args++;
608 }
609 for (i = 0 ; i < num_args; i++) {
610 if (arg_info[i].name) {
611 zend_string_release_ex(arg_info[i].name, 0);
612 }
613 zend_type_release(arg_info[i].type, /* persistent */ 0);
614 }
615 efree(arg_info);
616 }
617 if (op_array->static_variables) {
618 zend_array_destroy(op_array->static_variables);
619 }
620 if (op_array->num_dynamic_func_defs) {
621 for (i = 0; i < op_array->num_dynamic_func_defs; i++) {
622 /* Closures overwrite static_variables in their copy.
623 * Make sure to destroy them when the prototype function is destroyed. */
624 if (op_array->dynamic_func_defs[i]->static_variables
625 && (op_array->dynamic_func_defs[i]->fn_flags & ZEND_ACC_CLOSURE)) {
626 zend_array_destroy(op_array->dynamic_func_defs[i]->static_variables);
627 op_array->dynamic_func_defs[i]->static_variables = NULL;
628 }
629 destroy_op_array(op_array->dynamic_func_defs[i]);
630 }
631 efree(op_array->dynamic_func_defs);
632 }
633 }
634
zend_update_extended_stmts(zend_op_array * op_array)635 static void zend_update_extended_stmts(zend_op_array *op_array)
636 {
637 zend_op *opline = op_array->opcodes, *end=opline+op_array->last;
638
639 while (opline<end) {
640 if (opline->opcode == ZEND_EXT_STMT) {
641 if (opline+1<end) {
642 if ((opline+1)->opcode == ZEND_EXT_STMT) {
643 opline->opcode = ZEND_NOP;
644 opline++;
645 continue;
646 }
647 if (opline+1<end) {
648 opline->lineno = (opline+1)->lineno;
649 }
650 } else {
651 opline->opcode = ZEND_NOP;
652 }
653 }
654 opline++;
655 }
656 }
657
zend_extension_op_array_handler(zend_extension * extension,zend_op_array * op_array)658 static void zend_extension_op_array_handler(zend_extension *extension, zend_op_array *op_array)
659 {
660 if (extension->op_array_handler) {
661 extension->op_array_handler(op_array);
662 }
663 }
664
zend_check_finally_breakout(zend_op_array * op_array,uint32_t op_num,uint32_t dst_num)665 static void zend_check_finally_breakout(zend_op_array *op_array, uint32_t op_num, uint32_t dst_num)
666 {
667 int i;
668
669 for (i = 0; i < op_array->last_try_catch; i++) {
670 if ((op_num < op_array->try_catch_array[i].finally_op ||
671 op_num >= op_array->try_catch_array[i].finally_end)
672 && (dst_num >= op_array->try_catch_array[i].finally_op &&
673 dst_num <= op_array->try_catch_array[i].finally_end)) {
674 CG(in_compilation) = 1;
675 CG(active_op_array) = op_array;
676 CG(zend_lineno) = op_array->opcodes[op_num].lineno;
677 zend_error_noreturn(E_COMPILE_ERROR, "jump into a finally block is disallowed");
678 } else if ((op_num >= op_array->try_catch_array[i].finally_op
679 && op_num <= op_array->try_catch_array[i].finally_end)
680 && (dst_num > op_array->try_catch_array[i].finally_end
681 || dst_num < op_array->try_catch_array[i].finally_op)) {
682 CG(in_compilation) = 1;
683 CG(active_op_array) = op_array;
684 CG(zend_lineno) = op_array->opcodes[op_num].lineno;
685 zend_error_noreturn(E_COMPILE_ERROR, "jump out of a finally block is disallowed");
686 }
687 }
688 }
689
zend_get_brk_cont_target(const zend_op_array * op_array,const zend_op * opline)690 static uint32_t zend_get_brk_cont_target(const zend_op_array *op_array, const zend_op *opline) {
691 int nest_levels = opline->op2.num;
692 int array_offset = opline->op1.num;
693 zend_brk_cont_element *jmp_to;
694 do {
695 jmp_to = &CG(context).brk_cont_array[array_offset];
696 if (nest_levels > 1) {
697 array_offset = jmp_to->parent;
698 }
699 } while (--nest_levels > 0);
700
701 return opline->opcode == ZEND_BRK ? jmp_to->brk : jmp_to->cont;
702 }
703
emit_live_range_raw(zend_op_array * op_array,uint32_t var_num,uint32_t kind,uint32_t start,uint32_t end)704 static void emit_live_range_raw(
705 zend_op_array *op_array, uint32_t var_num, uint32_t kind, uint32_t start, uint32_t end) {
706 zend_live_range *range;
707
708 op_array->last_live_range++;
709 op_array->live_range = erealloc(op_array->live_range,
710 sizeof(zend_live_range) * op_array->last_live_range);
711
712 ZEND_ASSERT(start < end);
713 range = &op_array->live_range[op_array->last_live_range - 1];
714 range->var = EX_NUM_TO_VAR(op_array->last_var + var_num);
715 range->var |= kind;
716 range->start = start;
717 range->end = end;
718 }
719
emit_live_range(zend_op_array * op_array,uint32_t var_num,uint32_t start,uint32_t end,zend_needs_live_range_cb needs_live_range)720 static void emit_live_range(
721 zend_op_array *op_array, uint32_t var_num, uint32_t start, uint32_t end,
722 zend_needs_live_range_cb needs_live_range) {
723 zend_op *def_opline = &op_array->opcodes[start], *orig_def_opline = def_opline;
724 zend_op *use_opline = &op_array->opcodes[end];
725 uint32_t kind;
726
727 switch (def_opline->opcode) {
728 /* These should never be the first def. */
729 case ZEND_ADD_ARRAY_ELEMENT:
730 case ZEND_ADD_ARRAY_UNPACK:
731 case ZEND_ROPE_ADD:
732 ZEND_UNREACHABLE();
733 return;
734 /* Result is boolean, it doesn't have to be destroyed. */
735 case ZEND_JMPZ_EX:
736 case ZEND_JMPNZ_EX:
737 case ZEND_BOOL:
738 case ZEND_BOOL_NOT:
739 /* Classes don't have to be destroyed. */
740 case ZEND_FETCH_CLASS:
741 case ZEND_DECLARE_ANON_CLASS:
742 /* FAST_CALLs don't have to be destroyed. */
743 case ZEND_FAST_CALL:
744 return;
745 case ZEND_BEGIN_SILENCE:
746 kind = ZEND_LIVE_SILENCE;
747 start++;
748 break;
749 case ZEND_ROPE_INIT:
750 kind = ZEND_LIVE_ROPE;
751 /* ROPE live ranges include the generating opcode. */
752 def_opline--;
753 break;
754 case ZEND_FE_RESET_R:
755 case ZEND_FE_RESET_RW:
756 kind = ZEND_LIVE_LOOP;
757 start++;
758 break;
759 /* Objects created via ZEND_NEW are only fully initialized
760 * after the DO_FCALL (constructor call).
761 * We are creating two live-ranges: ZEND_LINE_NEW for uninitialized
762 * part, and ZEND_LIVE_TMPVAR for initialized.
763 */
764 case ZEND_NEW:
765 {
766 int level = 0;
767 uint32_t orig_start = start;
768
769 while (def_opline + 1 < use_opline) {
770 def_opline++;
771 start++;
772 switch (def_opline->opcode) {
773 case ZEND_INIT_FCALL:
774 case ZEND_INIT_FCALL_BY_NAME:
775 case ZEND_INIT_NS_FCALL_BY_NAME:
776 case ZEND_INIT_DYNAMIC_CALL:
777 case ZEND_INIT_USER_CALL:
778 case ZEND_INIT_METHOD_CALL:
779 case ZEND_INIT_STATIC_METHOD_CALL:
780 case ZEND_NEW:
781 level++;
782 break;
783 case ZEND_DO_FCALL:
784 case ZEND_DO_FCALL_BY_NAME:
785 case ZEND_DO_ICALL:
786 case ZEND_DO_UCALL:
787 if (level == 0) {
788 goto done;
789 }
790 level--;
791 break;
792 }
793 }
794 done:
795 emit_live_range_raw(op_array, var_num, ZEND_LIVE_NEW, orig_start + 1, start + 1);
796 if (start + 1 == end) {
797 /* Trivial live-range, no need to store it. */
798 return;
799 }
800 }
801 ZEND_FALLTHROUGH;
802 default:
803 start++;
804 kind = ZEND_LIVE_TMPVAR;
805
806 /* Check hook to determine whether a live range is necessary,
807 * e.g. based on type info. */
808 if (needs_live_range && !needs_live_range(op_array, orig_def_opline)) {
809 return;
810 }
811 break;
812 case ZEND_COPY_TMP:
813 {
814 /* COPY_TMP has a split live-range: One from the definition until the use in
815 * "null" branch, and another from the start of the "non-null" branch to the
816 * FREE opcode. */
817 uint32_t rt_var_num = EX_NUM_TO_VAR(op_array->last_var + var_num);
818 if (needs_live_range && !needs_live_range(op_array, orig_def_opline)) {
819 return;
820 }
821
822 kind = ZEND_LIVE_TMPVAR;
823 if (use_opline->opcode != ZEND_FREE) {
824 /* This can happen if one branch of the coalesce has been optimized away.
825 * In this case we should emit a normal live-range instead. */
826 start++;
827 break;
828 }
829
830 zend_op *block_start_op = use_opline;
831 while ((block_start_op-1)->opcode == ZEND_FREE) {
832 block_start_op--;
833 }
834
835 start = block_start_op - op_array->opcodes;
836 if (start != end) {
837 emit_live_range_raw(op_array, var_num, kind, start, end);
838 }
839
840 do {
841 use_opline--;
842
843 /* The use might have been optimized away, in which case we will hit the def
844 * instead. */
845 if (use_opline->opcode == ZEND_COPY_TMP && use_opline->result.var == rt_var_num) {
846 start = def_opline + 1 - op_array->opcodes;
847 emit_live_range_raw(op_array, var_num, kind, start, end);
848 return;
849 }
850 } while (!(
851 ((use_opline->op1_type & (IS_TMP_VAR|IS_VAR)) && use_opline->op1.var == rt_var_num) ||
852 ((use_opline->op2_type & (IS_TMP_VAR|IS_VAR)) && use_opline->op2.var == rt_var_num)
853 ));
854
855 start = def_opline + 1 - op_array->opcodes;
856 end = use_opline - op_array->opcodes;
857 emit_live_range_raw(op_array, var_num, kind, start, end);
858 return;
859 }
860 }
861
862 emit_live_range_raw(op_array, var_num, kind, start, end);
863 }
864
is_fake_def(zend_op * opline)865 static bool is_fake_def(zend_op *opline) {
866 /* These opcodes only modify the result, not create it. */
867 return opline->opcode == ZEND_ROPE_ADD
868 || opline->opcode == ZEND_ADD_ARRAY_ELEMENT
869 || opline->opcode == ZEND_ADD_ARRAY_UNPACK;
870 }
871
keeps_op1_alive(zend_op * opline)872 static bool keeps_op1_alive(zend_op *opline) {
873 /* These opcodes don't consume their OP1 operand,
874 * it is later freed by something else. */
875 if (opline->opcode == ZEND_CASE
876 || opline->opcode == ZEND_CASE_STRICT
877 || opline->opcode == ZEND_SWITCH_LONG
878 || opline->opcode == ZEND_SWITCH_STRING
879 || opline->opcode == ZEND_MATCH
880 || opline->opcode == ZEND_FETCH_LIST_R
881 || opline->opcode == ZEND_FETCH_LIST_W
882 || opline->opcode == ZEND_COPY_TMP) {
883 return 1;
884 }
885 ZEND_ASSERT(opline->opcode != ZEND_FE_FETCH_R
886 && opline->opcode != ZEND_FE_FETCH_RW
887 && opline->opcode != ZEND_VERIFY_RETURN_TYPE
888 && opline->opcode != ZEND_BIND_LEXICAL
889 && opline->opcode != ZEND_ROPE_ADD);
890 return 0;
891 }
892
893 /* Live ranges must be sorted by increasing start opline */
cmp_live_range(const zend_live_range * a,const zend_live_range * b)894 static int cmp_live_range(const zend_live_range *a, const zend_live_range *b) {
895 return a->start - b->start;
896 }
swap_live_range(zend_live_range * a,zend_live_range * b)897 static void swap_live_range(zend_live_range *a, zend_live_range *b) {
898 uint32_t tmp;
899 tmp = a->var;
900 a->var = b->var;
901 b->var = tmp;
902 tmp = a->start;
903 a->start = b->start;
904 b->start = tmp;
905 tmp = a->end;
906 a->end = b->end;
907 b->end = tmp;
908 }
909
zend_calc_live_ranges(zend_op_array * op_array,zend_needs_live_range_cb needs_live_range)910 static void zend_calc_live_ranges(
911 zend_op_array *op_array, zend_needs_live_range_cb needs_live_range) {
912 uint32_t opnum = op_array->last;
913 zend_op *opline = &op_array->opcodes[opnum];
914 ALLOCA_FLAG(use_heap)
915 uint32_t var_offset = op_array->last_var;
916 uint32_t *last_use = do_alloca(sizeof(uint32_t) * op_array->T, use_heap);
917 memset(last_use, -1, sizeof(uint32_t) * op_array->T);
918
919 ZEND_ASSERT(!op_array->live_range);
920 while (opnum > 0) {
921 opnum--;
922 opline--;
923
924 if ((opline->result_type & (IS_TMP_VAR|IS_VAR)) && !is_fake_def(opline)) {
925 uint32_t var_num = EX_VAR_TO_NUM(opline->result.var) - var_offset;
926 /* Defs without uses can occur for two reasons: Either because the result is
927 * genuinely unused (e.g. omitted FREE opcode for an unused boolean result), or
928 * because there are multiple defining opcodes (e.g. JMPZ_EX and QM_ASSIGN), in
929 * which case the last one starts the live range. As such, we can simply ignore
930 * missing uses here. */
931 if (EXPECTED(last_use[var_num] != (uint32_t) -1)) {
932 /* Skip trivial live-range */
933 if (opnum + 1 != last_use[var_num]) {
934 uint32_t num;
935
936 #if 1
937 /* OP_DATA uses only op1 operand */
938 ZEND_ASSERT(opline->opcode != ZEND_OP_DATA);
939 num = opnum;
940 #else
941 /* OP_DATA is really part of the previous opcode. */
942 num = opnum - (opline->opcode == ZEND_OP_DATA);
943 #endif
944 emit_live_range(op_array, var_num, num, last_use[var_num], needs_live_range);
945 }
946 last_use[var_num] = (uint32_t) -1;
947 }
948 }
949
950 if ((opline->op1_type & (IS_TMP_VAR|IS_VAR))) {
951 uint32_t var_num = EX_VAR_TO_NUM(opline->op1.var) - var_offset;
952 if (EXPECTED(last_use[var_num] == (uint32_t) -1)) {
953 if (EXPECTED(!keeps_op1_alive(opline))) {
954 /* OP_DATA is really part of the previous opcode. */
955 last_use[var_num] = opnum - (opline->opcode == ZEND_OP_DATA);
956 }
957 }
958 }
959 if (opline->op2_type & (IS_TMP_VAR|IS_VAR)) {
960 uint32_t var_num = EX_VAR_TO_NUM(opline->op2.var) - var_offset;
961 if (UNEXPECTED(opline->opcode == ZEND_FE_FETCH_R
962 || opline->opcode == ZEND_FE_FETCH_RW)) {
963 /* OP2 of FE_FETCH is actually a def, not a use. */
964 if (last_use[var_num] != (uint32_t) -1) {
965 if (opnum + 1 != last_use[var_num]) {
966 emit_live_range(
967 op_array, var_num, opnum, last_use[var_num], needs_live_range);
968 }
969 last_use[var_num] = (uint32_t) -1;
970 }
971 } else if (EXPECTED(last_use[var_num] == (uint32_t) -1)) {
972 #if 1
973 /* OP_DATA uses only op1 operand */
974 ZEND_ASSERT(opline->opcode != ZEND_OP_DATA);
975 last_use[var_num] = opnum;
976 #else
977 /* OP_DATA is really part of the previous opcode. */
978 last_use[var_num] = opnum - (opline->opcode == ZEND_OP_DATA);
979 #endif
980 }
981 }
982 }
983
984 if (op_array->last_live_range > 1) {
985 zend_live_range *r1 = op_array->live_range;
986 zend_live_range *r2 = r1 + op_array->last_live_range - 1;
987
988 /* In most cases we need just revert the array */
989 while (r1 < r2) {
990 swap_live_range(r1, r2);
991 r1++;
992 r2--;
993 }
994
995 r1 = op_array->live_range;
996 r2 = r1 + op_array->last_live_range - 1;
997 while (r1 < r2) {
998 if (r1->start > (r1+1)->start) {
999 zend_sort(r1, r2 - r1 + 1, sizeof(zend_live_range),
1000 (compare_func_t) cmp_live_range, (swap_func_t) swap_live_range);
1001 break;
1002 }
1003 r1++;
1004 }
1005 }
1006
1007 free_alloca(last_use, use_heap);
1008 }
1009
zend_recalc_live_ranges(zend_op_array * op_array,zend_needs_live_range_cb needs_live_range)1010 ZEND_API void zend_recalc_live_ranges(
1011 zend_op_array *op_array, zend_needs_live_range_cb needs_live_range) {
1012 /* We assume that we never create live-ranges where there were none before. */
1013 ZEND_ASSERT(op_array->live_range);
1014 efree(op_array->live_range);
1015 op_array->live_range = NULL;
1016 op_array->last_live_range = 0;
1017 zend_calc_live_ranges(op_array, needs_live_range);
1018 }
1019
pass_two(zend_op_array * op_array)1020 ZEND_API void pass_two(zend_op_array *op_array)
1021 {
1022 zend_op *opline, *end;
1023
1024 if (!ZEND_USER_CODE(op_array->type)) {
1025 return;
1026 }
1027 if (CG(compiler_options) & ZEND_COMPILE_EXTENDED_STMT) {
1028 zend_update_extended_stmts(op_array);
1029 }
1030 if (CG(compiler_options) & ZEND_COMPILE_HANDLE_OP_ARRAY) {
1031 if (zend_extension_flags & ZEND_EXTENSIONS_HAVE_OP_ARRAY_HANDLER) {
1032 zend_llist_apply_with_argument(&zend_extensions, (llist_apply_with_arg_func_t) zend_extension_op_array_handler, op_array);
1033 }
1034 }
1035
1036 if (CG(context).vars_size != op_array->last_var) {
1037 op_array->vars = (zend_string**) erealloc(op_array->vars, sizeof(zend_string*)*op_array->last_var);
1038 CG(context).vars_size = op_array->last_var;
1039 }
1040
1041 #if ZEND_USE_ABS_CONST_ADDR
1042 if (CG(context).opcodes_size != op_array->last) {
1043 op_array->opcodes = (zend_op *) erealloc(op_array->opcodes, sizeof(zend_op)*op_array->last);
1044 CG(context).opcodes_size = op_array->last;
1045 }
1046 if (CG(context).literals_size != op_array->last_literal) {
1047 op_array->literals = (zval*)erealloc(op_array->literals, sizeof(zval) * op_array->last_literal);
1048 CG(context).literals_size = op_array->last_literal;
1049 }
1050 #else
1051 op_array->opcodes = (zend_op *) erealloc(op_array->opcodes,
1052 ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16) +
1053 sizeof(zval) * op_array->last_literal);
1054 if (op_array->literals) {
1055 memcpy(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16),
1056 op_array->literals, sizeof(zval) * op_array->last_literal);
1057 efree(op_array->literals);
1058 op_array->literals = (zval*)(((char*)op_array->opcodes) + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op) * op_array->last, 16));
1059 }
1060 CG(context).opcodes_size = op_array->last;
1061 CG(context).literals_size = op_array->last_literal;
1062 #endif
1063
1064 op_array->T += ZEND_OBSERVER_ENABLED; // reserve last temporary for observers if enabled
1065
1066 /* Needs to be set directly after the opcode/literal reallocation, to ensure destruction
1067 * happens correctly if any of the following fixups generate a fatal error. */
1068 op_array->fn_flags |= ZEND_ACC_DONE_PASS_TWO;
1069
1070 opline = op_array->opcodes;
1071 end = opline + op_array->last;
1072 while (opline < end) {
1073 switch (opline->opcode) {
1074 case ZEND_RECV_INIT:
1075 {
1076 zval *val = CT_CONSTANT(opline->op2);
1077 if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
1078 uint32_t slot = ZEND_MM_ALIGNED_SIZE_EX(op_array->cache_size, 8);
1079 Z_CACHE_SLOT_P(val) = slot;
1080 op_array->cache_size += sizeof(zval);
1081 }
1082 }
1083 break;
1084 case ZEND_FAST_CALL:
1085 opline->op1.opline_num = op_array->try_catch_array[opline->op1.num].finally_op;
1086 ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1);
1087 break;
1088 case ZEND_BRK:
1089 case ZEND_CONT:
1090 {
1091 uint32_t jmp_target = zend_get_brk_cont_target(op_array, opline);
1092
1093 if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
1094 zend_check_finally_breakout(op_array, opline - op_array->opcodes, jmp_target);
1095 }
1096 opline->opcode = ZEND_JMP;
1097 opline->op1.opline_num = jmp_target;
1098 opline->op2.num = 0;
1099 ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1);
1100 }
1101 break;
1102 case ZEND_GOTO:
1103 zend_resolve_goto_label(op_array, opline);
1104 if (op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) {
1105 zend_check_finally_breakout(op_array, opline - op_array->opcodes, opline->op1.opline_num);
1106 }
1107 ZEND_FALLTHROUGH;
1108 case ZEND_JMP:
1109 ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op1);
1110 break;
1111 case ZEND_JMPZ:
1112 case ZEND_JMPNZ:
1113 case ZEND_JMPZ_EX:
1114 case ZEND_JMPNZ_EX:
1115 case ZEND_JMP_SET:
1116 case ZEND_COALESCE:
1117 case ZEND_FE_RESET_R:
1118 case ZEND_FE_RESET_RW:
1119 case ZEND_JMP_NULL:
1120 case ZEND_BIND_INIT_STATIC_OR_JMP:
1121 ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op2);
1122 break;
1123 case ZEND_ASSERT_CHECK:
1124 {
1125 /* If result of assert is unused, result of check is unused as well */
1126 zend_op *call = &op_array->opcodes[opline->op2.opline_num - 1];
1127 if (call->opcode == ZEND_EXT_FCALL_END) {
1128 call--;
1129 }
1130 if (call->result_type == IS_UNUSED) {
1131 opline->result_type = IS_UNUSED;
1132 }
1133 ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op2);
1134 break;
1135 }
1136 case ZEND_FE_FETCH_R:
1137 case ZEND_FE_FETCH_RW:
1138 /* absolute index to relative offset */
1139 opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value);
1140 break;
1141 case ZEND_CATCH:
1142 if (!(opline->extended_value & ZEND_LAST_CATCH)) {
1143 ZEND_PASS_TWO_UPDATE_JMP_TARGET(op_array, opline, opline->op2);
1144 }
1145 break;
1146 case ZEND_RETURN:
1147 case ZEND_RETURN_BY_REF:
1148 if (op_array->fn_flags & ZEND_ACC_GENERATOR) {
1149 opline->opcode = ZEND_GENERATOR_RETURN;
1150 }
1151 break;
1152 case ZEND_SWITCH_LONG:
1153 case ZEND_SWITCH_STRING:
1154 case ZEND_MATCH:
1155 {
1156 /* absolute indexes to relative offsets */
1157 HashTable *jumptable = Z_ARRVAL_P(CT_CONSTANT(opline->op2));
1158 zval *zv;
1159 ZEND_HASH_FOREACH_VAL(jumptable, zv) {
1160 Z_LVAL_P(zv) = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, Z_LVAL_P(zv));
1161 } ZEND_HASH_FOREACH_END();
1162
1163 opline->extended_value = ZEND_OPLINE_NUM_TO_OFFSET(op_array, opline, opline->extended_value);
1164 break;
1165 }
1166 }
1167 if (opline->op1_type == IS_CONST) {
1168 ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op1);
1169 } else if (opline->op1_type & (IS_VAR|IS_TMP_VAR)) {
1170 opline->op1.var = EX_NUM_TO_VAR(op_array->last_var + opline->op1.var);
1171 }
1172 if (opline->op2_type == IS_CONST) {
1173 ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op2);
1174 } else if (opline->op2_type & (IS_VAR|IS_TMP_VAR)) {
1175 opline->op2.var = EX_NUM_TO_VAR(op_array->last_var + opline->op2.var);
1176 }
1177 if (opline->result_type & (IS_VAR|IS_TMP_VAR)) {
1178 opline->result.var = EX_NUM_TO_VAR(op_array->last_var + opline->result.var);
1179 }
1180 ZEND_VM_SET_OPCODE_HANDLER(opline);
1181 opline++;
1182 }
1183
1184 zend_calc_live_ranges(op_array, NULL);
1185
1186 return;
1187 }
1188
get_unary_op(int opcode)1189 ZEND_API unary_op_type get_unary_op(int opcode)
1190 {
1191 switch (opcode) {
1192 case ZEND_BW_NOT:
1193 return (unary_op_type) bitwise_not_function;
1194 case ZEND_BOOL_NOT:
1195 return (unary_op_type) boolean_not_function;
1196 default:
1197 return (unary_op_type) NULL;
1198 }
1199 }
1200
get_binary_op(int opcode)1201 ZEND_API binary_op_type get_binary_op(int opcode)
1202 {
1203 switch (opcode) {
1204 case ZEND_ADD:
1205 return (binary_op_type) add_function;
1206 case ZEND_SUB:
1207 return (binary_op_type) sub_function;
1208 case ZEND_MUL:
1209 return (binary_op_type) mul_function;
1210 case ZEND_POW:
1211 return (binary_op_type) pow_function;
1212 case ZEND_DIV:
1213 return (binary_op_type) div_function;
1214 case ZEND_MOD:
1215 return (binary_op_type) mod_function;
1216 case ZEND_SL:
1217 return (binary_op_type) shift_left_function;
1218 case ZEND_SR:
1219 return (binary_op_type) shift_right_function;
1220 case ZEND_FAST_CONCAT:
1221 case ZEND_CONCAT:
1222 return (binary_op_type) concat_function;
1223 case ZEND_IS_IDENTICAL:
1224 case ZEND_CASE_STRICT:
1225 return (binary_op_type) is_identical_function;
1226 case ZEND_IS_NOT_IDENTICAL:
1227 return (binary_op_type) is_not_identical_function;
1228 case ZEND_IS_EQUAL:
1229 case ZEND_CASE:
1230 return (binary_op_type) is_equal_function;
1231 case ZEND_IS_NOT_EQUAL:
1232 return (binary_op_type) is_not_equal_function;
1233 case ZEND_IS_SMALLER:
1234 return (binary_op_type) is_smaller_function;
1235 case ZEND_IS_SMALLER_OR_EQUAL:
1236 return (binary_op_type) is_smaller_or_equal_function;
1237 case ZEND_SPACESHIP:
1238 return (binary_op_type) compare_function;
1239 case ZEND_BW_OR:
1240 return (binary_op_type) bitwise_or_function;
1241 case ZEND_BW_AND:
1242 return (binary_op_type) bitwise_and_function;
1243 case ZEND_BW_XOR:
1244 return (binary_op_type) bitwise_xor_function;
1245 case ZEND_BOOL_XOR:
1246 return (binary_op_type) boolean_xor_function;
1247 default:
1248 ZEND_UNREACHABLE();
1249 return (binary_op_type) NULL;
1250 }
1251 }
1252