1 /*
2 +----------------------------------------------------------------------+
3 | Zend Engine, Func Info |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 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 | https://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: Dmitry Stogov <dmitry@php.net> |
16 | Xinchen Hui <laruence@php.net> |
17 +----------------------------------------------------------------------+
18 */
19
20 #include "zend_compile.h"
21 #include "zend_extensions.h"
22 #include "zend_ssa.h"
23 #include "zend_optimizer_internal.h"
24 #include "zend_inference.h"
25 #include "zend_call_graph.h"
26 #include "zend_func_info.h"
27 #include "zend_inference.h"
28 #ifdef _WIN32
29 #include "win32/ioutil.h"
30 #endif
31
32 typedef uint32_t (*info_func_t)(const zend_call_info *call_info, const zend_ssa *ssa);
33
34 typedef struct _func_info_t {
35 const char *name;
36 unsigned name_len;
37 uint32_t info;
38 info_func_t info_func;
39 } func_info_t;
40
41 #define F0(name, info) \
42 {name, sizeof(name)-1, (info), NULL}
43 #define F1(name, info) \
44 {name, sizeof(name)-1, (MAY_BE_RC1 | (info)), NULL}
45 #define FN(name, info) \
46 {name, sizeof(name)-1, (MAY_BE_RC1 | MAY_BE_RCN | (info)), NULL}
47 #define FC(name, callback) \
48 {name, sizeof(name)-1, 0, callback}
49
50 #include "zend_func_infos.h"
51
zend_range_info(const zend_call_info * call_info,const zend_ssa * ssa)52 static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa *ssa)
53 {
54 if (!call_info->send_unpack
55 && (call_info->num_args == 2 || call_info->num_args == 3)
56 && ssa
57 && !(ssa->cfg.flags & ZEND_SSA_TSSA)) {
58 zend_op_array *op_array = call_info->caller_op_array;
59 uint32_t t1 = _ssa_op1_info(op_array, ssa, call_info->arg_info[0].opline,
60 &ssa->ops[call_info->arg_info[0].opline - op_array->opcodes]);
61 uint32_t t2 = _ssa_op1_info(op_array, ssa, call_info->arg_info[1].opline,
62 &ssa->ops[call_info->arg_info[1].opline - op_array->opcodes]);
63 uint32_t t3 = 0;
64 uint32_t tmp = MAY_BE_RC1 | MAY_BE_ARRAY;
65
66 if (call_info->num_args == 3) {
67 t3 = _ssa_op1_info(op_array, ssa, call_info->arg_info[2].opline,
68 &ssa->ops[call_info->arg_info[2].opline - op_array->opcodes]);
69 }
70 if ((t1 & MAY_BE_STRING) && (t2 & MAY_BE_STRING)) {
71 tmp |= MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING;
72 }
73 if ((t1 & (MAY_BE_DOUBLE|MAY_BE_STRING))
74 || (t2 & (MAY_BE_DOUBLE|MAY_BE_STRING))
75 || (t3 & (MAY_BE_DOUBLE|MAY_BE_STRING))) {
76 tmp |= MAY_BE_ARRAY_OF_DOUBLE;
77 }
78 if ((t1 & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_DOUBLE))
79 && (t2 & ((MAY_BE_ANY|MAY_BE_UNDEF)-MAY_BE_DOUBLE))) {
80 tmp |= MAY_BE_ARRAY_OF_LONG;
81 }
82 if (tmp & MAY_BE_ARRAY_OF_ANY) {
83 tmp |= MAY_BE_ARRAY_PACKED;
84 }
85 return tmp;
86 } else {
87 /* May throw */
88 return MAY_BE_RC1 | MAY_BE_ARRAY | MAY_BE_ARRAY_EMPTY | MAY_BE_ARRAY_PACKED | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING;
89 }
90 }
91
92 static const func_info_t old_func_infos[] = {
93 FC("range", zend_range_info),
94 };
95
96 static HashTable func_info;
97 ZEND_API int zend_func_info_rid = -1;
98
zend_get_internal_func_info(const zend_function * callee_func,const zend_call_info * call_info,const zend_ssa * ssa)99 uint32_t zend_get_internal_func_info(
100 const zend_function *callee_func, const zend_call_info *call_info, const zend_ssa *ssa) {
101 if (callee_func->common.scope) {
102 /* This is a method, not a function. */
103 return 0;
104 }
105
106 zend_string *name = callee_func->common.function_name;
107 if (!name) {
108 /* zend_pass_function has no name. */
109 return 0;
110 }
111
112 zval *zv = zend_hash_find_known_hash(&func_info, name);
113 if (!zv) {
114 return 0;
115 }
116
117 func_info_t *info = Z_PTR_P(zv);
118 if (info->info_func) {
119 return call_info ? info->info_func(call_info, ssa) : 0;
120 } else {
121 uint32_t ret = info->info;
122
123 if (ret & MAY_BE_ARRAY) {
124 ret |= MAY_BE_ARRAY_EMPTY;
125 }
126 return ret;
127 }
128 }
129
zend_get_func_info(const zend_call_info * call_info,const zend_ssa * ssa,zend_class_entry ** ce,bool * ce_is_instanceof)130 ZEND_API uint32_t zend_get_func_info(
131 const zend_call_info *call_info, const zend_ssa *ssa,
132 zend_class_entry **ce, bool *ce_is_instanceof)
133 {
134 uint32_t ret = 0;
135 const zend_function *callee_func = call_info->callee_func;
136 *ce = NULL;
137 *ce_is_instanceof = 0;
138
139 if (callee_func->type == ZEND_INTERNAL_FUNCTION) {
140 uint32_t internal_ret = zend_get_internal_func_info(callee_func, call_info, ssa);
141 #if !ZEND_DEBUG
142 if (internal_ret) {
143 return internal_ret;
144 }
145 #endif
146
147 ret = zend_get_return_info_from_signature_only(
148 callee_func, /* script */ NULL, ce, ce_is_instanceof, /* use_tentative_return_info */ !call_info->is_prototype);
149
150 #if ZEND_DEBUG
151 if (internal_ret) {
152 zend_string *name = callee_func->common.function_name;
153 /* Check whether the func_info information is a subset of the information we can
154 * compute from the specified return type, otherwise it contains redundant types. */
155 if (internal_ret & ~ret) {
156 fprintf(stderr, "Inaccurate func info for %s()\n", ZSTR_VAL(name));
157 }
158 /* Check whether the func info is completely redundant with arginfo. */
159 if (internal_ret == ret) {
160 fprintf(stderr, "Useless func info for %s()\n", ZSTR_VAL(name));
161 }
162 /* If the return type is not mixed, check that the types match exactly if we exclude
163 * RC and array information. */
164 uint32_t ret_any = ret & MAY_BE_ANY, internal_ret_any = internal_ret & MAY_BE_ANY;
165 if (ret_any != MAY_BE_ANY) {
166 uint32_t diff = internal_ret_any ^ ret_any;
167 /* Func info may contain "true" types as well as isolated "null" and "false". */
168 if (diff && !(diff == MAY_BE_FALSE && (ret & MAY_BE_FALSE))
169 && (internal_ret_any & ~(MAY_BE_NULL|MAY_BE_FALSE))) {
170 fprintf(stderr, "Incorrect func info for %s()\n", ZSTR_VAL(name));
171 }
172 }
173 return internal_ret;
174 }
175 #endif
176 } else {
177 if (!call_info->is_prototype) {
178 // FIXME: the order of functions matters!!!
179 zend_func_info *info = ZEND_FUNC_INFO((zend_op_array*)callee_func);
180 if (info) {
181 ret = info->return_info.type;
182 *ce = info->return_info.ce;
183 *ce_is_instanceof = info->return_info.is_instanceof;
184 }
185 }
186 if (!ret) {
187 ret = zend_get_return_info_from_signature_only(
188 callee_func, /* TODO: script */ NULL, ce, ce_is_instanceof, /* use_tentative_return_info */ !call_info->is_prototype);
189 /* It's allowed to override a method that return non-reference with a method that returns a reference */
190 if (call_info->is_prototype && (ret & ~MAY_BE_REF)) {
191 ret |= MAY_BE_REF;
192 *ce = NULL;
193 }
194 }
195 }
196 return ret;
197 }
198
zend_func_info_add(const func_info_t * func_infos,size_t n)199 static void zend_func_info_add(const func_info_t *func_infos, size_t n)
200 {
201 for (size_t i = 0; i < n; i++) {
202 zend_string *key = zend_string_init_interned(func_infos[i].name, func_infos[i].name_len, 1);
203
204 if (zend_hash_add_ptr(&func_info, key, (void**)&func_infos[i]) == NULL) {
205 fprintf(stderr, "ERROR: Duplicate function info for \"%s\"\n", func_infos[i].name);
206 }
207
208 zend_string_release_ex(key, 1);
209 }
210 }
211
zend_func_info_startup(void)212 zend_result zend_func_info_startup(void)
213 {
214 if (zend_func_info_rid == -1) {
215 zend_func_info_rid = zend_get_resource_handle("Zend Optimizer");
216 if (zend_func_info_rid < 0) {
217 return FAILURE;
218 }
219
220 zend_hash_init(&func_info, sizeof(old_func_infos)/sizeof(func_info_t) + sizeof(func_infos)/sizeof(func_info_t), NULL, NULL, 1);
221
222 zend_func_info_add(old_func_infos, sizeof(old_func_infos)/sizeof(func_info_t));
223 zend_func_info_add(func_infos, sizeof(func_infos)/sizeof(func_info_t));
224 }
225
226 return SUCCESS;
227 }
228
zend_func_info_shutdown(void)229 zend_result zend_func_info_shutdown(void)
230 {
231 if (zend_func_info_rid != -1) {
232 zend_hash_destroy(&func_info);
233 zend_func_info_rid = -1;
234 }
235 return SUCCESS;
236 }
237