fix leak with strings!
[m6w6/ext-psi] / src / module.c
1
2 #ifdef HAVE_CONFIG_H
3 #include "config.h"
4 #endif
5
6 #include "php.h"
7 #include "php_ini.h"
8 #include "ext/standard/info.h"
9
10 #include "php_psi.h"
11 #include "parser.h"
12
13 #include "libjit.h"
14
15 ZEND_DECLARE_MODULE_GLOBALS(psi);
16
17 PHP_INI_BEGIN()
18 STD_PHP_INI_ENTRY("psi.directory", "psis", PHP_INI_ALL, OnUpdateString, directory, zend_psi_globals, psi_globals)
19 PHP_INI_END();
20
21 void psi_error(int type, const char *msg, ...)
22 {
23 char buf[0x1000];
24 va_list argv;
25
26 va_start(argv, msg);
27 vslprintf(buf, 0x1000, msg, argv);
28 va_end(argv);
29
30 php_error(type, buf);
31 }
32
33 int psi_internal_type(impl_type *type)
34 {
35 switch (type->type) {
36 case PSI_T_BOOL:
37 return _IS_BOOL;
38 case PSI_T_INT:
39 case PSI_T_LONG:
40 return IS_LONG;
41 case PSI_T_FLOAT:
42 case PSI_T_DOUBLE:
43 return IS_DOUBLE;
44 case PSI_T_STRING:
45 return IS_STRING;
46 case PSI_T_ARRAY:
47 return IS_ARRAY;
48 default:
49 return 0;
50 }
51 }
52
53 zend_internal_arg_info *psi_internal_arginfo(impl *impl)
54 {
55 size_t i;
56 zend_internal_arg_info *aip;
57 zend_internal_function_info *fi;
58
59 aip = calloc(impl->func->args->count + 1, sizeof(*aip));
60
61 fi = (zend_internal_function_info *) &aip[0];
62 fi->required_num_args = psi_num_min_args(impl);
63 fi->return_reference = impl->func->return_reference;
64 fi->type_hint = psi_internal_type(impl->func->return_type);
65
66 for (i = 0; i < impl->func->args->count; ++i) {
67 impl_arg *iarg = impl->func->args->args[i];
68 zend_internal_arg_info *ai = &aip[i+1];
69
70 ai->name = iarg->var->name;
71 ai->type_hint = psi_internal_type(iarg->type);
72 if (iarg->var->reference) {
73 ai->pass_by_reference = 1;
74 }
75 if (iarg->var->reference || (iarg->def && iarg->def->type == PSI_T_NULL)) {
76 ai->allow_null = 1;
77 }
78 }
79
80 return aip;
81 }
82
83 size_t psi_num_min_args(impl *impl)
84 {
85 size_t i, n = impl->func->args->count;
86
87 for (i = 0; i < impl->func->args->count; ++i) {
88 if (impl->func->args->args[i]->def) {
89 --n;
90 }
91 }
92 return n;
93 }
94
95 void psi_to_int(impl_val *ret_val, decl_arg *func, zval *return_value)
96 {
97 switch (real_decl_type(func->type)->type) {
98 case PSI_T_FLOAT:
99 case PSI_T_DOUBLE:
100 RETVAL_DOUBLE(deref_impl_val(0, ret_val, func)->dval);
101 convert_to_long(return_value);
102 break;
103 default:
104 RETVAL_LONG(deref_impl_val(0, ret_val, func)->lval);
105 }
106 }
107
108 void psi_to_string(impl_val *ret_val, decl_arg *func, zval *return_value)
109 {
110 switch (real_decl_type(func->type)->type) {
111 case PSI_T_CHAR:
112 case PSI_T_SINT8:
113 case PSI_T_UINT8:
114 if (!func->var->pointer_level) {
115 char chr = ret_val->lval;
116 RETVAL_STRINGL(&chr, 1);
117 } else {
118 ret_val = deref_impl_val(1, ret_val, func);
119 if (ret_val->ptr) {
120 RETVAL_STRING(ret_val->ptr);
121 } else {
122 RETVAL_EMPTY_STRING();
123 }
124 }
125 break;
126 case PSI_T_FLOAT:
127 case PSI_T_DOUBLE:
128 RETVAL_DOUBLE(deref_impl_val(0, ret_val, func)->dval);
129 convert_to_string(return_value);
130 break;
131 default:
132 RETVAL_LONG(deref_impl_val(0, ret_val, func)->lval);
133 convert_to_string(return_value);
134 break;
135 }
136 }
137
138 ZEND_RESULT_CODE psi_parse_args(zend_execute_data *execute_data, impl *impl)
139 {
140 impl_arg *iarg;
141
142 if (!impl->func->args->count) {
143 return zend_parse_parameters_none();
144 }
145
146 ZEND_PARSE_PARAMETERS_START(psi_num_min_args(impl), impl->func->args->count)
147 nextarg:
148 iarg = impl->func->args->args[_i];
149 if (iarg->def) {
150 Z_PARAM_OPTIONAL;
151 }
152 if (PSI_T_BOOL == iarg->type->type) {
153 if (iarg->def) {
154 iarg->val.bval = iarg->def->type == PSI_T_TRUE ? 1 : 0;
155 }
156 Z_PARAM_BOOL(iarg->val.bval);
157 } else if (PSI_T_INT == iarg->type->type) {
158 if (iarg->def) {
159 iarg->val.lval = zend_atol(iarg->def->text, strlen(iarg->def->text));
160 }
161 Z_PARAM_LONG(iarg->val.lval);
162 } else if (PSI_T_FLOAT == iarg->type->type) {
163 if (iarg->def) {
164 iarg->val.dval = zend_strtod(iarg->def->text, NULL);
165 }
166 Z_PARAM_DOUBLE(iarg->val.dval);
167 } else if (PSI_T_STRING == iarg->type->type) {
168 struct {char *val; size_t len;} str;
169 if (iarg->def) {
170 /* FIXME */
171 str.len = strlen(iarg->def->text) - 2;
172 str.val = &iarg->def->text[1];
173 }
174 Z_PARAM_STR_EX(iarg->val.str, 1, 0);
175 if (iarg->val.str) {
176 zend_string_addref(iarg->val.str);
177 } else if (iarg->def) {
178 iarg->val.str = zend_string_init(str.val, str.len, 0);
179 }
180 } else {
181 error_code = ZPP_ERROR_FAILURE;
182 break;
183 }
184 iarg->_zv = _arg;
185 if (_i < _max_num_args) {
186 goto nextarg;
187 }
188 ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
189
190 return SUCCESS;
191 }
192
193 impl_val *psi_do_let(decl_arg *darg)
194 {
195 impl_val *arg_val = &darg->let->out;
196 impl_arg *iarg = darg->let->arg;
197
198 if (!iarg) {
199 /* let foo = NULL */
200 memset(arg_val, 0, sizeof(*arg_val));
201 return arg_val;
202 }
203 switch (darg->let->val->func->type) {
204 case PSI_T_BOOLVAL:
205 if (iarg->type->type == PSI_T_BOOL) {
206 arg_val->cval = iarg->val.cval;
207 } else {
208 arg_val->cval = zend_is_true(iarg->_zv);
209 }
210 break;
211 case PSI_T_INTVAL:
212 if (iarg->type->type == PSI_T_INT) {
213 arg_val->lval = iarg->val.lval;
214 } else {
215 arg_val->lval = zval_get_long(iarg->_zv);
216 }
217 break;
218 case PSI_T_STRVAL:
219 if (iarg->type->type == PSI_T_STRING) {
220 arg_val->ptr = estrdup(iarg->val.str->val);
221 darg->let->mem = arg_val->ptr;
222 zend_string_release(iarg->val.str);
223 } else {
224 zend_string *zs = zval_get_string(iarg->_zv);
225 arg_val->ptr = estrdup(zs->val);
226 darg->let->mem = arg_val->ptr;
227 zend_string_release(zs);
228 }
229 break;
230 case PSI_T_STRLEN:
231 if (iarg->type->type == PSI_T_STRING) {
232 arg_val->lval = iarg->val.str->len;
233 zend_string_release(iarg->val.str);
234 } else {
235 zend_string *zs = zval_get_string(iarg->_zv);
236 arg_val->lval = zs->len;
237 zend_string_release(zs);
238 }
239 break;
240 EMPTY_SWITCH_DEFAULT_CASE();
241 }
242
243 return arg_val;
244 }
245
246 void psi_do_set(zval *return_value, set_func *func, decl_vars *vars)
247 {
248 impl_val *val = (impl_val *) &vars->vars[0]->arg->let->ptr;
249
250 ZVAL_DEREF(return_value);
251 zval_dtor(return_value);
252
253 switch (func->type) {
254 case PSI_T_TO_STRING:
255 psi_to_string(val, vars->vars[0]->arg, return_value);
256 break;
257 EMPTY_SWITCH_DEFAULT_CASE();
258 }
259 }
260
261 void psi_do_return(impl *impl, impl_val *ret_val, zval *return_value)
262 {
263 switch (impl->stmts->ret.list[0]->func->type) {
264 case PSI_T_TO_STRING:
265 psi_to_string(ret_val, impl->decl->func, return_value);
266 break;
267 case PSI_T_TO_INT:
268 psi_to_int(ret_val, impl->decl->func, return_value);
269 break;
270 EMPTY_SWITCH_DEFAULT_CASE();
271 }
272 }
273
274 void psi_do_free(free_stmt *fre)
275 {
276 size_t i;
277
278 for (i = 0; i < fre->vars->count; ++i) {
279 decl_var *dvar = fre->vars->vars[i];
280
281 if (dvar->arg && dvar->arg->let->out.ptr) {
282 free(dvar->arg->let->out.ptr);
283 dvar->arg->let->out.ptr = NULL;
284 }
285 }
286 }
287
288 void psi_do_clean(impl *impl)
289 {
290 size_t i;
291
292 for (i = 0; i < impl->func->args->count; ++i ) {
293 impl_arg *iarg = impl->func->args->args[i];
294
295 switch (iarg->type->type) {
296 case PSI_T_STRING:
297 if (iarg->val.str) {
298 zend_string_release(iarg->val.str);
299 }
300 break;
301 }
302 }
303
304 for (i = 0; i < impl->decl->args->count; ++i) {
305 decl_arg *darg = impl->decl->args->args[i];
306
307 if (darg->let && darg->let->mem) {
308 efree(darg->let->mem);
309 darg->let->mem = NULL;
310 }
311 }
312 }
313
314 PHP_MINIT_FUNCTION(psi)
315 {
316 REGISTER_INI_ENTRIES();
317
318 PSI_ContextInit(&PSI_G(context), PSI_Libjit(), psi_error);
319 PSI_ContextBuild(&PSI_G(context), PSI_G(directory));
320
321 return SUCCESS;
322 }
323 PHP_MSHUTDOWN_FUNCTION(psi)
324 {
325 PSI_ContextDtor(&PSI_G(context));
326
327 UNREGISTER_INI_ENTRIES();
328
329 return SUCCESS;
330 }
331
332 /* Remove if there's nothing to do at request start */
333 /* {{{ PHP_RINIT_FUNCTION
334 */
335 PHP_RINIT_FUNCTION(psi)
336 {
337 #if defined(COMPILE_DL_PSI) && defined(ZTS)
338 ZEND_TSRMLS_CACHE_UPDATE();
339 #endif
340 return SUCCESS;
341 }
342 /* }}} */
343
344 /* Remove if there's nothing to do at request end */
345 /* {{{ PHP_RSHUTDOWN_FUNCTION
346 */
347 PHP_RSHUTDOWN_FUNCTION(psi)
348 {
349 return SUCCESS;
350 }
351 /* }}} */
352
353 PHP_MINFO_FUNCTION(psi)
354 {
355 php_info_print_table_start();
356 php_info_print_table_header(2, "psi support", "enabled");
357 php_info_print_table_end();
358
359 DISPLAY_INI_ENTRIES();
360 }
361 const zend_function_entry psi_functions[] = {
362 PHP_FE_END
363 };
364
365 zend_module_entry psi_module_entry = {
366 STANDARD_MODULE_HEADER,
367 "psi",
368 psi_functions,
369 PHP_MINIT(psi),
370 PHP_MSHUTDOWN(psi),
371 PHP_RINIT(psi), /* Replace with NULL if there's nothing to do at request start */
372 PHP_RSHUTDOWN(psi), /* Replace with NULL if there's nothing to do at request end */
373 PHP_MINFO(psi),
374 PHP_PSI_VERSION,
375 STANDARD_MODULE_PROPERTIES
376 };
377
378 #ifdef COMPILE_DL_PSI
379 #ifdef ZTS
380 ZEND_TSRMLS_CACHE_DEFINE();
381 #endif
382 ZEND_GET_MODULE(psi)
383 #endif
384
385 /*
386 * Local variables:
387 * tab-width: 4
388 * c-basic-offset: 4
389 * End:
390 * vim600: noet sw=4 ts=4 fdm=marker
391 * vim<600: noet sw=4 ts=4
392 */