3e95970cca7cdf6babd0af1665d693d7ca772966
[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 #include "zend_exceptions.h"
10 #include "zend_constants.h"
11 #include "zend_operators.h"
12
13 #include "php_psi.h"
14
15 #if HAVE_LIBJIT
16 # include "libjit.h"
17 # ifndef HAVE_LIBFFI
18 # define PSI_ENGINE "jit"
19 # endif
20 #endif
21 #if HAVE_LIBFFI
22 # include "libffi.h"
23 # define PSI_ENGINE "ffi"
24 #endif
25
26 #include <ndbm.h>
27
28 ZEND_DECLARE_MODULE_GLOBALS(psi);
29
30 PHP_INI_BEGIN()
31 STD_PHP_INI_ENTRY("psi.engine", PSI_ENGINE, PHP_INI_SYSTEM, OnUpdateString, engine, zend_psi_globals, psi_globals)
32 STD_PHP_INI_ENTRY("psi.directory", "psi.d", PHP_INI_SYSTEM, OnUpdateString, directory, zend_psi_globals, psi_globals)
33 PHP_INI_END();
34
35 static zend_object_handlers psi_object_handlers;
36 static zend_class_entry *psi_class_entry;
37
38 void psi_error_wrapper(PSI_Token *t, int type, const char *msg, ...)
39 {
40 va_list argv;
41
42 va_start(argv, msg);
43 psi_verror(type, t?t->file:"Unknown", t?*t->line:0, msg, argv);
44 va_end(argv);
45 }
46 void psi_error(int type, const char *fn, unsigned ln, const char *msg, ...)
47 {
48 va_list argv;
49
50 va_start(argv, msg);
51 psi_verror(type, fn, ln, msg, argv);
52 va_end(argv);
53 }
54 void psi_verror(int type, const char *fn, unsigned ln, const char *msg, va_list argv)
55 {
56 zend_error_cb(type, fn, ln, msg, argv);
57 }
58
59 size_t psi_t_alignment(token_t t)
60 {
61 #define PSI_ALIGNOF(T) case PSI_T_## T: return ALIGNOF_## T ##_T;
62 switch (t) {
63 PSI_ALIGNOF(INT8);
64 PSI_ALIGNOF(UINT8);
65 PSI_ALIGNOF(INT16);
66 PSI_ALIGNOF(UINT16);
67 PSI_ALIGNOF(INT32);
68 PSI_ALIGNOF(UINT32);
69 PSI_ALIGNOF(INT64);
70 PSI_ALIGNOF(UINT64);
71 case PSI_T_FLOAT:
72 return ALIGNOF_FLOAT;
73 case PSI_T_DOUBLE:
74 return ALIGNOF_DOUBLE;
75 case PSI_T_POINTER:
76 return ALIGNOF_VOID_P;
77 EMPTY_SWITCH_DEFAULT_CASE();
78 }
79 return 0;
80 }
81
82 size_t psi_t_size(token_t t)
83 {
84 #define PSI_SIZEOF(T) case PSI_T_## T : return SIZEOF_## T ##_T;
85 switch (t) {
86 PSI_SIZEOF(INT8);
87 PSI_SIZEOF(UINT8);
88 PSI_SIZEOF(INT16);
89 PSI_SIZEOF(UINT16);
90 PSI_SIZEOF(INT32);
91 PSI_SIZEOF(UINT32);
92 PSI_SIZEOF(INT64);
93 PSI_SIZEOF(UINT64);
94 case PSI_T_FLOAT:
95 return SIZEOF_FLOAT;
96 case PSI_T_DOUBLE:
97 return SIZEOF_DOUBLE;
98 case PSI_T_POINTER:
99 return SIZEOF_VOID_P;
100 EMPTY_SWITCH_DEFAULT_CASE();
101 }
102 return 0;
103 }
104
105 size_t psi_t_align(token_t t, size_t s)
106 {
107 size_t a = psi_t_alignment(t);
108 return ((s - 1) | (a - 1)) + 1;
109 }
110
111 int psi_internal_type(impl_type *type)
112 {
113 switch (type->type) {
114 case PSI_T_BOOL:
115 return _IS_BOOL;
116 case PSI_T_INT:
117 return IS_LONG;
118 case PSI_T_FLOAT:
119 case PSI_T_DOUBLE:
120 return IS_DOUBLE;
121 case PSI_T_STRING:
122 return IS_STRING;
123 case PSI_T_ARRAY:
124 return IS_ARRAY;
125 default:
126 return 0;
127 }
128 }
129
130 zend_internal_arg_info *psi_internal_arginfo(impl *impl)
131 {
132 size_t i;
133 zend_internal_arg_info *aip;
134 zend_internal_function_info *fi;
135
136 aip = calloc(impl->func->args->count + 1 + !!impl->func->args->vararg.name, sizeof(*aip));
137
138 fi = (zend_internal_function_info *) &aip[0];
139 fi->allow_null = 1;
140 fi->required_num_args = psi_num_min_args(impl);
141 fi->return_reference = impl->func->return_reference;
142 fi->type_hint = psi_internal_type(impl->func->return_type);
143
144 if (impl->func->args->vararg.name) {
145 impl_arg *vararg = impl->func->args->vararg.name;
146 zend_internal_arg_info *ai = &aip[impl->func->args->count];
147
148 ai->name = vararg->var->name;
149 ai->allow_null = 1;
150 ai->type_hint = psi_internal_type(vararg->type);
151 if (vararg->var->reference) {
152 ai->pass_by_reference = 1;
153 }
154 ai->is_variadic = 1;
155 }
156
157 for (i = 0; i < impl->func->args->count; ++i) {
158 impl_arg *iarg = impl->func->args->args[i];
159 zend_internal_arg_info *ai = &aip[i+1];
160
161 ai->name = iarg->var->name;
162 ai->type_hint = psi_internal_type(iarg->type);
163 if (iarg->var->reference) {
164 ai->pass_by_reference = 1;
165 }
166 //if (iarg->var->reference || (iarg->def && iarg->def->type == PSI_T_NULL)) {
167 ai->allow_null = 1;
168 //}
169 }
170
171 return aip;
172 }
173
174 size_t psi_num_min_args(impl *impl)
175 {
176 size_t i, n = impl->func->args->count;
177
178 for (i = 0; i < impl->func->args->count; ++i) {
179 if (impl->func->args->args[i]->def) {
180 --n;
181 }
182 }
183 return n;
184 }
185
186 void psi_to_void(zval *return_value, set_value *set, impl_val *ret_val)
187 {
188 RETVAL_NULL();
189 }
190
191 void psi_to_bool(zval *return_value, set_value *set, impl_val *ret_val)
192 {
193 psi_to_int(return_value, set, ret_val);
194 convert_to_boolean(return_value);
195 }
196
197 void psi_to_int(zval *return_value, set_value *set, impl_val *ret_val)
198 {
199 decl_var *var = set->vars->vars[0];
200 token_t t = real_decl_type(var->arg->type)->type;
201 impl_val *v = deref_impl_val(ret_val, var);
202
203 switch (t) {
204 case PSI_T_FLOAT:
205 RETVAL_DOUBLE((double) v->fval);
206 convert_to_long(return_value);
207 break;
208 case PSI_T_DOUBLE:
209 RETVAL_DOUBLE(v->dval);
210 convert_to_long(return_value);
211 break;
212 case PSI_T_INT8:
213 RETVAL_LONG(v->i8);
214 break;
215 case PSI_T_UINT8:
216 RETVAL_LONG(v->u8);
217 break;
218 case PSI_T_INT16:
219 RETVAL_LONG(v->i16);
220 break;
221 case PSI_T_UINT16:
222 RETVAL_LONG(v->u16);
223 break;
224 case PSI_T_INT32:
225 RETVAL_LONG(v->i32);
226 break;
227 case PSI_T_UINT32:
228 #if UINT32_MAX >= ZEND_LONG_MAX
229 if (v->u32 > ZEND_LONG_MAX) {
230 char d[12] = {0};
231
232 RETVAL_STRING(zend_print_ulong_to_buf(&d[10], v->u32));
233 } else {
234 #endif
235 RETVAL_LONG(v->u32);
236 #if UINT32_MAX >= ZEND_LONG_MAX
237 }
238 #endif
239 break;
240 case PSI_T_INT64:
241 RETVAL_LONG(v->i64);
242 break;
243 case PSI_T_UINT64:
244 if (v->u64 > ZEND_LONG_MAX) {
245 char d[24] = {0};
246
247 RETVAL_STRING(zend_print_ulong_to_buf(&d[22], v->u64));
248 } else {
249 RETVAL_LONG(v->u64);
250 }
251 break;
252 EMPTY_SWITCH_DEFAULT_CASE();
253 }
254 }
255
256 void psi_to_double(zval *return_value, set_value *set, impl_val *ret_val)
257 {
258 decl_var *var = set->vars->vars[0];
259 token_t t = real_decl_type(var->arg->type)->type;
260 impl_val *v = deref_impl_val(ret_val, var);
261
262 switch (t) {
263 case PSI_T_FLOAT:
264 RETVAL_DOUBLE((double) v->fval);
265 break;
266 case PSI_T_DOUBLE:
267 RETVAL_DOUBLE(v->dval);
268 break;
269 case PSI_T_INT8:
270 RETVAL_DOUBLE((double) v->i8);
271 break;
272 case PSI_T_UINT8:
273 RETVAL_DOUBLE((double) v->u8);
274 break;
275 case PSI_T_INT16:
276 RETVAL_DOUBLE((double) v->i16);
277 break;
278 case PSI_T_UINT16:
279 RETVAL_DOUBLE((double) v->u16);
280 break;
281 case PSI_T_INT32:
282 RETVAL_DOUBLE((double) v->i32);
283 break;
284 case PSI_T_UINT32:
285 RETVAL_DOUBLE((double) v->u32);
286 break;
287 case PSI_T_INT64:
288 RETVAL_DOUBLE((double) v->i64);
289 break;
290 case PSI_T_UINT64:
291 RETVAL_DOUBLE((double) v->u64);
292 break;
293 EMPTY_SWITCH_DEFAULT_CASE();
294 }
295 }
296
297 void psi_to_string(zval *return_value, set_value *set, impl_val *ret_val)
298 {
299 char *str;
300 decl_var *var = set->vars->vars[0];
301 token_t t = real_decl_type(var->arg->type)->type;
302
303 switch (t) {
304 case PSI_T_FLOAT:
305 RETVAL_DOUBLE((double) deref_impl_val(ret_val, var)->fval);
306 break;
307 case PSI_T_DOUBLE:
308 RETVAL_DOUBLE(deref_impl_val(ret_val, var)->dval);
309 break;
310 default:
311 if (!var->arg->var->pointer_level) {
312 RETVAL_STRINGL(&ret_val->cval, 1);
313 } else {
314 ret_val = deref_impl_val(ret_val, var);
315 if (var->arg->var->array_size) {
316 str = (char *) ret_val;
317 } else {
318 str = ret_val->ptr;
319 }
320 if (str) {
321 if (set->num) {
322 zend_long n = psi_long_num_exp(set->num, set->outer.val);
323 RETVAL_STRINGL(str, n);
324 } else {
325 RETVAL_STRING(str);
326 }
327 } else {
328 RETVAL_EMPTY_STRING();
329 }
330 }
331 return;
332 }
333 convert_to_string(return_value);
334 }
335
336
337 static impl_val *iterate(impl_val *val, size_t size, unsigned i, impl_val *tmp)
338 {
339 memset(tmp, 0, sizeof(*tmp));
340 memcpy(tmp, ((void*) val) + size * i, size);
341 return tmp;
342 }
343
344 void psi_from_zval(impl_val *mem, decl_arg *spec, zval *zv, void **tmp)
345 {
346 decl_type *type = real_decl_type(spec->type);
347
348 switch (type->type) {
349 case PSI_T_FLOAT:
350 mem->fval = (float) zval_get_double(zv);
351 break;
352 case PSI_T_DOUBLE:
353 mem->dval = zval_get_double(zv);
354 break;
355 case PSI_T_VOID:
356 case PSI_T_INT8:
357 case PSI_T_UINT8:
358 if (spec->var->pointer_level) {
359 zend_string *zs = zval_get_string(zv);
360 *tmp = mem->ptr = estrndup(zs->val, zs->len);
361 zend_string_release(zs);
362 break;
363 }
364 /* no break */
365 default:
366 mem->zend.lval = zval_get_long(zv);
367 break;
368 }
369 }
370
371 void *psi_array_to_struct(decl_struct *s, HashTable *arr)
372 {
373 size_t i, j = 0;
374 char *mem = ecalloc(1, s->size + s->args->count * sizeof(void *));
375
376 if (arr) for (i = 0; i < s->args->count; ++i) {
377 decl_arg *darg = s->args->args[i];
378 zval *entry = zend_hash_str_find_ind(arr, darg->var->name, strlen(darg->var->name));
379
380 if (entry) {
381 impl_val val;
382 void *tmp = NULL;
383
384 memset(&tmp, 0, sizeof(tmp));
385 psi_from_zval(&val, darg, entry, &tmp);
386 memcpy(mem + darg->layout->pos, &val, darg->layout->len);
387 if (tmp) {
388 ((void **)(mem + s->size))[j++] = tmp;
389 }
390 }
391 }
392 return mem;
393 }
394
395 void psi_to_recursive(zval *return_value, set_value *set, impl_val *r_val)
396 {
397 set->outer.set->func->handler(return_value, set, r_val);
398 }
399
400 void psi_to_array(zval *return_value, set_value *set, impl_val *r_val)
401 {
402 size_t i;
403 decl_var *var = set->vars->vars[0];
404 token_t t = real_decl_type(var->arg->type)->type;
405 impl_val tmp, *ret_val = deref_impl_val(r_val, var);
406
407 if ((intptr_t) ret_val <= (intptr_t) 0) {
408 RETURN_NULL();
409 }
410
411 array_init(return_value);
412
413 if (t == PSI_T_STRUCT) {
414 // decl_struct *s = real_decl_type(var->arg->type)->strct;
415
416 if (set->count) {
417 /* explicit member casts */
418 for (i = 0; i < set->count; ++i) {
419 set_value *sub_set = set->inner[i];
420 decl_var *sub_var = sub_set->vars->vars[0];
421
422 sub_set->outer.val = ret_val;
423
424 if (sub_var->arg) {
425 impl_val *tmp = NULL, *val;
426 zval ztmp;
427
428 val = struct_member_ref(sub_var->arg, ret_val, &tmp);
429 sub_set->func->handler(&ztmp, sub_set, val);
430 add_assoc_zval(return_value, sub_var->name, &ztmp);
431
432 if (tmp) {
433 free(tmp);
434 }
435 }
436 }
437 }
438 return;
439 }
440
441 if (var->arg->var->array_size) {
442 /* to_array(foo[NUMBER]) */
443 for (i = 0; i < var->arg->var->array_size; ++i) {
444 size_t size = psi_t_size(var->arg->var->pointer_level > 1 ? PSI_T_POINTER : t);
445 impl_val *ptr = iterate(ret_val, size, i, &tmp);
446 zval ele;
447
448 switch (t) {
449 case PSI_T_FLOAT:
450 ZVAL_DOUBLE(&ele, (double) ptr->fval);
451 break;
452 case PSI_T_DOUBLE:
453 ZVAL_DOUBLE(&ele, ptr->dval);
454 break;
455 default:
456 ZVAL_LONG(&ele, ptr->lval);
457 break;
458 }
459
460 add_next_index_zval(return_value, &ele);
461 }
462 return;
463 } else if (set->num) {
464 /* to_array(arr_var, num_expr, to_int(*arr_var)) */
465 zval ele;
466 char *ptr;
467 zend_long i, n = psi_long_num_exp(set->num, set->outer.val);
468 size_t size = psi_t_size(var->arg->var->pointer_level ? PSI_T_POINTER : t);
469 set_value *sub_set = set->inner[0];
470
471 sub_set->outer.val = set->outer.val;
472 for (i = 0; i < n; ++i) {
473 ptr = (char *) ret_val->ptr + i * size;
474 sub_set->func->handler(&ele, sub_set, (void *) ptr);
475 add_next_index_zval(return_value, &ele);
476 }
477 } else {
478 /* to_array(arr_var, to_int(*arr_var)) */
479 zval ele;
480 char *ptr = ret_val->ptr;
481 size_t size = psi_t_size(var->arg->var->pointer_level ? PSI_T_POINTER : t);
482 set_value *sub_set = set->inner[0];
483
484 sub_set->outer.val = set->outer.val;
485 while (*(void **) ptr) {
486 sub_set->func->handler(&ele, sub_set, (void *) ptr);
487 add_next_index_zval(return_value, &ele);
488 ptr += size;
489 }
490 }
491 }
492
493 void psi_to_object(zval *return_value, set_value *set, impl_val *r_val)
494 {
495 decl_var *var = set->vars->vars[0];
496 impl_val *ret_val = deref_impl_val(r_val, var);
497 psi_object *obj;
498
499 if ((intptr_t) ret_val->ptr > (intptr_t) 0) {
500 object_init_ex(return_value, psi_class_entry);
501 obj = PSI_OBJ(return_value, NULL);
502 obj->data = ret_val->ptr;
503 } else {
504 RETVAL_NULL();
505 }
506 }
507
508 static inline ZEND_RESULT_CODE psi_parse_args(zend_execute_data *execute_data, impl *impl)
509 {
510 size_t i;
511 impl_arg *iarg;
512 zend_error_handling zeh;
513
514 zend_replace_error_handling(EH_THROW, zend_exception_get_default(), &zeh);
515
516 if (!impl->func->args->count) {
517 ZEND_RESULT_CODE rv;
518
519 rv = zend_parse_parameters_none();
520 zend_restore_error_handling(&zeh);
521 return rv;
522 }
523
524 ZEND_PARSE_PARAMETERS_START(psi_num_min_args(impl), impl->func->args->vararg.name ? -1 : impl->func->args->count)
525 nextarg:
526 if (impl->func->args->vararg.name && _i >= impl->func->args->count) {
527 impl_arg *varg = impl->func->args->vararg.name;
528 iarg = init_impl_arg(
529 init_impl_type(varg->type->type, varg->type->name),
530 init_impl_var(varg->var->name, varg->var->reference),
531 NULL);
532
533 Z_PARAM_OPTIONAL;
534 if (_i == impl->func->args->count) {
535 impl->func->args->vararg.args = init_impl_args(iarg);
536 } else {
537 add_impl_arg(impl->func->args->vararg.args, iarg);
538 }
539 } else {
540 iarg = impl->func->args->args[_i];
541 if (iarg->def) {
542 Z_PARAM_OPTIONAL;
543 }
544 }
545 if (PSI_T_BOOL == iarg->type->type) {
546 Z_PARAM_BOOL(iarg->val.zend.bval);
547 } else if (PSI_T_INT == iarg->type->type) {
548 Z_PARAM_LONG(iarg->val.zend.lval);
549 } else if (PSI_T_FLOAT == iarg->type->type || PSI_T_DOUBLE == iarg->type->type) {
550 Z_PARAM_DOUBLE(iarg->val.dval);
551 } else if (PSI_T_STRING == iarg->type->type) {
552 Z_PARAM_STR_EX(iarg->val.zend.str, 1, iarg->var->reference);
553 if (iarg->val.zend.str) {
554 zend_string_addref(iarg->val.zend.str);
555 }
556 } else if (PSI_T_ARRAY == iarg->type->type) {
557 Z_PARAM_PROLOGUE(0);
558 } else if (PSI_T_OBJECT == iarg->type->type) {
559 Z_PARAM_PROLOGUE(0);
560 } else if (PSI_T_MIXED == iarg->type->type) {
561 Z_PARAM_PROLOGUE(0);
562 } else {
563 error_code = ZPP_ERROR_FAILURE;
564 break;
565 }
566 iarg->_zv = _arg;
567 ZVAL_DEREF(iarg->_zv);
568 if (_i < _num_args) {
569 goto nextarg;
570 }
571 ZEND_PARSE_PARAMETERS_END_EX(
572 zend_restore_error_handling(&zeh);
573 return FAILURE
574 );
575
576 /* set up defaults */
577 for (i = 0; i < impl->func->args->count; ++i) {
578 if (i >= EX_NUM_ARGS() && iarg->def) {
579 iarg = impl->func->args->args[i];
580
581 switch (iarg->type->type) {
582 case PSI_T_BOOL:
583 iarg->val.zend.bval = iarg->def->type == PSI_T_TRUE ? 1 : 0;
584 break;
585 case PSI_T_INT:
586 iarg->val.zend.lval = zend_atol(iarg->def->text, strlen(iarg->def->text));
587 break;
588 case PSI_T_FLOAT:
589 case PSI_T_DOUBLE:
590 iarg->val.dval = zend_strtod(iarg->def->text, NULL);
591 break;
592 case PSI_T_STRING:
593 /* FIXME */
594 iarg->val.zend.str = zend_string_init(&iarg->def->text[1], strlen(iarg->def->text) - 2, 0);
595 break;
596 }
597 }
598 }
599
600 zend_restore_error_handling(&zeh);
601 return SUCCESS;
602 }
603
604 static inline void *psi_do_calloc(let_calloc *alloc)
605 {
606 zend_long n = psi_long_num_exp(alloc->nmemb, NULL), s = psi_long_num_exp(alloc->size, NULL);
607 void *mem = safe_emalloc(n, s, sizeof(void *));
608 memset(mem, 0, n * s + sizeof(void *));
609 #if 0
610 fprintf(stderr, "calloc: %p\n", mem);
611 #endif
612 return mem;
613 }
614
615 static inline impl_val *psi_let_val(token_t let_func, impl_arg *iarg, impl_val *arg_val, decl_struct *strct, void **to_free)
616 {
617 switch (let_func) {
618 case PSI_T_BOOLVAL:
619 if (iarg->type->type == PSI_T_BOOL) {
620 arg_val->cval = iarg->val.zend.bval;
621 } else {
622 arg_val->cval = zend_is_true(iarg->_zv);
623 }
624 break;
625 case PSI_T_INTVAL:
626 if (iarg->type->type == PSI_T_INT) {
627 arg_val->lval = iarg->val.zend.lval;
628 } else {
629 arg_val->lval = zval_get_long(iarg->_zv);
630 }
631 break;
632 case PSI_T_FLOATVAL:
633 if (iarg->type->type == PSI_T_FLOAT || iarg->type->type == PSI_T_DOUBLE) {
634 arg_val->dval = iarg->val.dval;
635 } else {
636 arg_val->dval = zval_get_double(iarg->_zv);
637 }
638 break;
639 case PSI_T_PATHVAL:
640 case PSI_T_STRVAL:
641 if (iarg->type->type == PSI_T_STRING) {
642 if (iarg->val.zend.str) {
643 arg_val->ptr = estrndup(iarg->val.zend.str->val, iarg->val.zend.str->len);
644 *to_free = arg_val->ptr;
645 } else {
646 arg_val->ptr = "";
647 }
648 } else {
649 zend_string *zs = zval_get_string(iarg->_zv);
650 arg_val->ptr = estrdup(zs->val);
651 *to_free = arg_val->ptr;
652 zend_string_release(zs);
653 }
654 if (PSI_T_PATHVAL == let_func) {
655 if (SUCCESS != php_check_open_basedir(arg_val->ptr)) {
656 efree(arg_val->ptr);
657 return NULL;
658 }
659 }
660 break;
661 case PSI_T_STRLEN:
662 if (iarg->type->type == PSI_T_STRING) {
663 if (iarg->val.zend.str) {
664 arg_val->lval = iarg->val.zend.str->len;
665 } else {
666 arg_val->lval = 0;
667 }
668 } else {
669 zend_string *zs = zval_get_string(iarg->_zv);
670 arg_val->lval = zs->len;
671 zend_string_release(zs);
672 }
673 break;
674 case PSI_T_ARRVAL:
675 if (iarg->type->type == PSI_T_ARRAY) {
676 arg_val = psi_array_to_struct(strct, HASH_OF(iarg->_zv));
677 *to_free = arg_val;
678 }
679 break;
680 case PSI_T_OBJVAL:
681 if (iarg->type->type == PSI_T_OBJECT) {
682 psi_object *obj;
683
684 if (!instanceof_function(Z_OBJCE_P(iarg->_zv), psi_class_entry)) {
685 return NULL;
686 }
687
688 obj = PSI_OBJ(iarg->_zv, NULL);
689 arg_val->ptr = obj->data;
690 }
691 break;
692 EMPTY_SWITCH_DEFAULT_CASE();
693 }
694 return arg_val;
695 }
696
697 static inline void *psi_do_let(let_stmt *let)
698 {
699 decl_arg *darg = let->var->arg;
700 impl_val *arg_val = darg->ptr;
701 impl_arg *iarg;
702
703 switch (let->val ? let->val->kind : PSI_LET_NULL) {
704 case PSI_LET_TMP:
705 memcpy(arg_val, deref_impl_val(let->val->data.var->arg->let->ptr, let->val->data.var), sizeof(*arg_val));
706 #if 0
707 fprintf(stderr, "LET TMP: %p -> %p\n",
708 let->val->data.var->arg->let->ptr,
709 arg_val->ptr);
710 #endif
711 break;
712 case PSI_LET_NULL:
713 if (darg->var->array_size) {
714 arg_val->ptr = ecalloc(darg->var->array_size, sizeof(*arg_val));
715 darg->mem = arg_val->ptr;
716 } else {
717 memset(arg_val, 0, sizeof(*arg_val));
718 }
719 break;
720 case PSI_LET_CALLOC:
721 arg_val->ptr = psi_do_calloc(let->val->data.alloc);
722 darg->mem = arg_val->ptr;
723 break;
724 case PSI_LET_NUMEXP:
725 arg_val->zend.lval = psi_long_num_exp(let->val->data.num, NULL);
726 break;
727 case PSI_LET_FUNC:
728 iarg = let->val->data.func->arg;
729
730 if (!(darg->ptr = psi_let_val(let->val->data.func->type, iarg, darg->ptr, real_decl_type(darg->type)->strct, &darg->mem))) {
731 return NULL;
732 }
733 }
734
735 if (let->val && let->val->flags.one.is_reference) {
736 return let->ptr = &darg->ptr;
737 } else {
738 return let->ptr = darg->ptr;
739 }
740 }
741
742 static inline void psi_do_set(zval *return_value, set_value *set)
743 {
744 decl_arg *set_arg = set->vars->vars[0]->arg;
745
746 zval_dtor(return_value);
747 set->func->handler(return_value, set, set_arg->let ? set_arg->let->ptr : set_arg->ptr);
748 }
749
750 static inline void psi_do_return(zval *return_value, return_stmt *ret)
751 {
752 ret->set->func->handler(return_value, ret->set, ret->set->vars->vars[0]->arg->ptr);
753 }
754
755 static inline void psi_do_free(free_stmt *fre)
756 {
757 size_t i, j;
758
759 for (i = 0; i < fre->calls->count; ++i) {
760 free_call *f = fre->calls->list[i];
761
762 for (j = 0; j < f->vars->count; ++j) {
763 decl_var *dvar = f->vars->vars[j];
764 decl_arg *darg = dvar->arg;
765 impl_val *fval = darg->let ? darg->let->ptr : darg->ptr;
766
767 f->decl->call.args[j] = deref_impl_val(fval, dvar);
768 }
769
770 /* FIXME: check in validate_* that free functions return scalar */
771 PSI_ContextCall(&PSI_G(context), &f->decl->call, NULL);
772 }
773 }
774
775 static inline void psi_clean_array_struct(decl_arg *darg) {
776 if (darg->let
777 && darg->let->val->kind == PSI_LET_FUNC
778 && darg->let->val->data.func->type == PSI_T_ARRVAL) {
779 decl_type *type = real_decl_type(darg->type);
780
781 if (type->type == PSI_T_STRUCT) {
782 void **ptr = (void **) ((char *) darg->mem + type->strct->size);
783
784 while (*ptr) {
785 efree(*ptr++);
786 }
787 }
788 }
789 }
790
791 static inline void psi_do_clean(impl *impl)
792 {
793 size_t i;
794
795 if (impl->decl->func->ptr != &impl->decl->func->val) {
796 efree(impl->decl->func->ptr);
797 impl->decl->func->ptr = &impl->decl->func->val;
798 }
799 for (i = 0; i < impl->func->args->count; ++i ) {
800 impl_arg *iarg = impl->func->args->args[i];
801
802 switch (iarg->type->type) {
803 case PSI_T_STRING:
804 if (iarg->val.zend.str) {
805 zend_string_release(iarg->val.zend.str);
806 }
807 break;
808 }
809 }
810
811 if (impl->decl->args) for (i = 0; i < impl->decl->args->count; ++i) {
812 decl_arg *darg = impl->decl->args->args[i];
813
814 if (darg->mem) {
815 psi_clean_array_struct(darg);
816 efree(darg->mem);
817 darg->mem = NULL;
818 }
819 darg->ptr = &darg->val;
820 }
821
822 if (impl->func->args->vararg.args) {
823 free_impl_args(impl->func->args->vararg.args);
824 impl->func->args->vararg.args = NULL;
825 }
826 if (impl->func->args->vararg.types) {
827 efree(impl->func->args->vararg.types);
828 impl->func->args->vararg.types = NULL;
829 }
830 if (impl->func->args->vararg.values) {
831 efree(impl->func->args->vararg.values);
832 impl->func->args->vararg.values = NULL;
833 }
834 if (impl->func->args->vararg.free_list) {
835 void **list = impl->func->args->vararg.free_list;
836
837 while (*list) {
838 efree(*list++);
839 }
840
841 efree(impl->func->args->vararg.free_list);
842 impl->func->args->vararg.free_list = NULL;
843 }
844 }
845
846 static inline int psi_calc_num_exp_value(num_exp *exp, impl_val *strct, impl_val *res) {
847 impl_val *ref, *tmp = NULL;
848
849 switch (exp->t) {
850 case PSI_T_NUMBER:
851 switch (is_numeric_string(exp->u.numb, strlen(exp->u.numb), (zend_long *) res, (double *) res, 0)) {
852 case IS_LONG:
853 return PSI_T_INT64;
854 case IS_DOUBLE:
855 return PSI_T_DOUBLE;
856 }
857 break;
858
859 case PSI_T_NSNAME:
860 switch (exp->u.cnst->type->type) {
861 case PSI_T_INT:
862 res->i64 = zend_get_constant_str(exp->u.cnst->name, strlen(exp->u.cnst->name))->value.lval;
863 return PSI_T_INT64;
864 case PSI_T_FLOAT:
865 res->dval = zend_get_constant_str(exp->u.cnst->name, strlen(exp->u.cnst->name))->value.dval;
866 return PSI_T_DOUBLE;
867 default:
868 return 0;
869 }
870 break;
871
872 case PSI_T_NAME:
873 if (strct) {
874 ref = struct_member_ref(exp->u.dvar->arg, strct, &tmp);
875 } else if (exp->u.dvar->arg->let) {
876 ref = exp->u.dvar->arg->let->ptr;
877 } else {
878 ref = exp->u.dvar->arg->ptr;
879 }
880 switch (real_decl_type(exp->u.dvar->arg->type)->type) {
881 case PSI_T_INT8:
882 case PSI_T_UINT8:
883 case PSI_T_INT16:
884 case PSI_T_UINT16:
885 case PSI_T_INT32:
886 case PSI_T_UINT32:
887 case PSI_T_INT64:
888 case PSI_T_UINT64:
889 memcpy(res, deref_impl_val(ref, exp->u.dvar), sizeof(*res));
890 if (tmp) {
891 free(tmp);
892 }
893 return real_decl_type(exp->u.dvar->arg->type)->type;
894
895 case PSI_T_FLOAT:
896 case PSI_T_DOUBLE:
897 memcpy(res, deref_impl_val(ref, exp->u.dvar), sizeof(*res));
898 if (tmp) {
899 free(tmp);
900 }
901 return real_decl_type(exp->u.dvar->arg->type)->type;
902
903 EMPTY_SWITCH_DEFAULT_CASE();
904 }
905 break;
906
907 EMPTY_SWITCH_DEFAULT_CASE();
908 }
909 return 0;
910 }
911
912 int psi_calc_num_exp(num_exp *exp, impl_val *strct, impl_val *res) {
913 impl_val num = {0};
914 int num_type = psi_calc_num_exp_value(exp, strct, &num);
915
916 if (exp->operand) {
917 impl_val tmp = {0};
918 int tmp_type = psi_calc_num_exp(exp->operand, strct, &tmp);
919
920 return exp->calculator(num_type, &num, tmp_type, &tmp, res);
921 }
922
923 memcpy(res, &num, sizeof(*res));
924 return num_type;
925 }
926
927 #define PRIfval "f"
928 #define PRIdval "lf"
929
930 #define PSI_CALC_OP(var) do { \
931 const char *fmt = "calc %" PRI##var ", %" PRI##var ": %" PRI##var "\n"; \
932 res->var = PSI_CALC(v1->var, v2->var); \
933 if (!res->var) fprintf(stderr, fmt, v1->var, v2->var, res->var); \
934 } while (0)
935 #define PSI_CALC_OP2(vres, var1, var2) do { \
936 const char *fmt = "calc %" PRI##var1 ", %" PRI##var2 ": %" PRI##vres "\n"; \
937 res->vres = PSI_CALC(v1->var1, v2->var2); \
938 if (!res->vres) fprintf(stderr, fmt, v1->var1, v2->var2, res->vres); \
939 } while(0)
940 #define PSI_CALC_FN(op) int psi_calc_##op(int t1, impl_val *v1, int t2, impl_val *v2, impl_val *res) \
941 { \
942 if (t1 == t2) { \
943 switch (t1) { \
944 case PSI_T_FLOAT: PSI_CALC_OP(fval); break; \
945 case PSI_T_DOUBLE: PSI_CALC_OP(dval); break; \
946 case PSI_T_INT8: PSI_CALC_OP(i8); break; \
947 case PSI_T_UINT8: PSI_CALC_OP(u8); break; \
948 case PSI_T_INT16: PSI_CALC_OP(i16); break; \
949 case PSI_T_UINT16: PSI_CALC_OP(u16); break; \
950 case PSI_T_INT32: PSI_CALC_OP(i32); break; \
951 case PSI_T_UINT32: PSI_CALC_OP(u32); break; \
952 case PSI_T_INT64: PSI_CALC_OP(i64); break; \
953 case PSI_T_UINT64: PSI_CALC_OP(u64); break; \
954 EMPTY_SWITCH_DEFAULT_CASE(); \
955 } \
956 return t1; \
957 } else if (t1 == PSI_T_DOUBLE) { \
958 switch (t2) { \
959 case PSI_T_FLOAT: PSI_CALC_OP2(dval, dval, fval); break; \
960 case PSI_T_INT8: PSI_CALC_OP2(dval, dval, i8); break; \
961 case PSI_T_UINT8: PSI_CALC_OP2(dval, dval, u8); break; \
962 case PSI_T_INT16: PSI_CALC_OP2(dval, dval, i16); break; \
963 case PSI_T_UINT16: PSI_CALC_OP2(dval, dval, u16); break; \
964 case PSI_T_INT32: PSI_CALC_OP2(dval, dval, i32); break; \
965 case PSI_T_UINT32: PSI_CALC_OP2(dval, dval, u32); break; \
966 case PSI_T_INT64: PSI_CALC_OP2(dval, dval, i64); break; \
967 case PSI_T_UINT64: PSI_CALC_OP2(dval, dval, u64); break; \
968 EMPTY_SWITCH_DEFAULT_CASE(); \
969 } \
970 return t1; \
971 } else if (t2 == PSI_T_DOUBLE) { \
972 switch (t1) { \
973 case PSI_T_FLOAT: PSI_CALC_OP2(dval, fval, dval); break; \
974 case PSI_T_INT8: PSI_CALC_OP2(dval, i8, dval); break; \
975 case PSI_T_UINT8: PSI_CALC_OP2(dval, u8, dval); break; \
976 case PSI_T_INT16: PSI_CALC_OP2(dval, i16, dval); break; \
977 case PSI_T_UINT16: PSI_CALC_OP2(dval, u16, dval); break; \
978 case PSI_T_INT32: PSI_CALC_OP2(dval, i32, dval); break; \
979 case PSI_T_UINT32: PSI_CALC_OP2(dval, u32, dval); break; \
980 case PSI_T_INT64: PSI_CALC_OP2(dval, i64, dval); break; \
981 case PSI_T_UINT64: PSI_CALC_OP2(dval, u64, dval); break; \
982 EMPTY_SWITCH_DEFAULT_CASE(); \
983 } \
984 return t2; \
985 } else if (t1 == PSI_T_FLOAT) { \
986 switch (t2) { \
987 case PSI_T_DOUBLE: PSI_CALC_OP2(dval, fval, dval); return t2; \
988 case PSI_T_INT8: PSI_CALC_OP2(fval, fval, i8); break; \
989 case PSI_T_UINT8: PSI_CALC_OP2(fval, fval, u8); break; \
990 case PSI_T_INT16: PSI_CALC_OP2(fval, fval, i16); break; \
991 case PSI_T_UINT16: PSI_CALC_OP2(fval, fval, u16); break; \
992 case PSI_T_INT32: PSI_CALC_OP2(fval, fval, i32); break; \
993 case PSI_T_UINT32: PSI_CALC_OP2(fval, fval, u32); break; \
994 case PSI_T_INT64: PSI_CALC_OP2(fval, fval, i64); break; \
995 case PSI_T_UINT64: PSI_CALC_OP2(fval, fval, u64); break; \
996 EMPTY_SWITCH_DEFAULT_CASE(); \
997 } \
998 return t1; \
999 } else if (t2 == PSI_T_FLOAT) { \
1000 switch (t1) { \
1001 case PSI_T_DOUBLE: PSI_CALC_OP2(dval, dval, fval); return t1; \
1002 case PSI_T_INT8: PSI_CALC_OP2(fval, i8, fval); break; \
1003 case PSI_T_UINT8: PSI_CALC_OP2(fval, u8, fval); break; \
1004 case PSI_T_INT16: PSI_CALC_OP2(fval, i16, fval); break; \
1005 case PSI_T_UINT16: PSI_CALC_OP2(fval, u16, fval); break; \
1006 case PSI_T_INT32: PSI_CALC_OP2(fval, i32, fval); break; \
1007 case PSI_T_UINT32: PSI_CALC_OP2(fval, u32, fval); break; \
1008 case PSI_T_INT64: PSI_CALC_OP2(fval, i64, fval); break; \
1009 case PSI_T_UINT64: PSI_CALC_OP2(fval, u64, fval); break; \
1010 EMPTY_SWITCH_DEFAULT_CASE(); \
1011 } \
1012 return t2; \
1013 } else { \
1014 int64_t sval1 = v1->i64, sval2 = v2->i64; \
1015 uint64_t uval1 = v1->u64, uval2 = v2->u64; \
1016 switch (t1) { \
1017 case PSI_T_INT8: sval1 >>= 8; \
1018 case PSI_T_INT16: sval1 >>= 8; \
1019 case PSI_T_INT32: sval1 >>= 8; \
1020 case PSI_T_INT64: \
1021 switch (t2) { \
1022 case PSI_T_INT8: sval2 >>= 8; \
1023 case PSI_T_INT16: sval2 >>= 8; \
1024 case PSI_T_INT32: sval2 >>= 8; \
1025 case PSI_T_INT64: \
1026 res->i64 = PSI_CALC(sval1 , sval2); \
1027 return PSI_T_INT64; \
1028 case PSI_T_UINT8: uval2 >>= 8; \
1029 case PSI_T_UINT16: uval2 >>= 8; \
1030 case PSI_T_UINT32: uval2 >>= 8; \
1031 case PSI_T_UINT64: \
1032 res->i64 = PSI_CALC(sval1, uval2); \
1033 return PSI_T_INT64; \
1034 } \
1035 break; \
1036 case PSI_T_UINT8: uval1 >>= 8; \
1037 case PSI_T_UINT16: uval1 >>= 8; \
1038 case PSI_T_UINT32: uval1 >>= 8; \
1039 case PSI_T_UINT64: \
1040 switch (t2) { \
1041 case PSI_T_INT8: sval2 >>= 8; \
1042 case PSI_T_INT16: sval2 >>= 8; \
1043 case PSI_T_INT32: sval2 >>= 8; \
1044 case PSI_T_INT64: \
1045 res->i64 = PSI_CALC(uval1, sval2); \
1046 return PSI_T_INT64; \
1047 case PSI_T_UINT8: uval2 >>= 8; \
1048 case PSI_T_UINT16: uval2 >>= 8; \
1049 case PSI_T_UINT32: uval2 >>= 8; \
1050 case PSI_T_UINT64: \
1051 res->u64 = PSI_CALC(uval1, uval2); \
1052 return PSI_T_UINT64; \
1053 } \
1054 break; \
1055 } \
1056 } \
1057 ZEND_ASSERT(0); \
1058 return 0; \
1059 }
1060
1061 #undef PSI_CALC
1062 #define PSI_CALC(var1, var2) (var1) + (var2)
1063 PSI_CALC_FN(add)
1064 #undef PSI_CALC
1065 #define PSI_CALC(var1, var2) (var1) * (var2)
1066 PSI_CALC_FN(mul)
1067 #undef PSI_CALC
1068 #define PSI_CALC(var1, var2) (var1) - (var2)
1069 PSI_CALC_FN(sub)
1070 #undef PSI_CALC
1071 #define PSI_CALC(var1, var2) (var1) / (var2)
1072 PSI_CALC_FN(div)
1073
1074 static inline void psi_do_args(impl *impl) {
1075 size_t i;
1076
1077 for (i = 0; i < impl->decl->args->count; ++i) {
1078 impl->decl->call.args[i] = impl->decl->args->args[i]->let->ptr;
1079 }
1080
1081 if (!impl->decl->func->var->pointer_level) {
1082 decl_type *real = real_decl_type(impl->decl->func->type);
1083
1084 switch (real->type) {
1085 case PSI_T_STRUCT:
1086 impl->decl->func->ptr = psi_array_to_struct(real->strct, NULL);
1087 break;
1088 }
1089 }
1090 }
1091
1092 static inline impl_vararg *psi_do_varargs(impl *impl) {
1093 size_t i, j;
1094 impl_vararg *va = &impl->func->args->vararg;
1095 size_t vacount = va->args->count;
1096
1097
1098 if (!vacount) {
1099 return NULL;
1100 }
1101
1102 va->types = ecalloc(vacount, sizeof(*va->types));
1103 va->values = ecalloc(vacount, sizeof(*va->values));
1104
1105 for (i = 0, j = 0; i < vacount; ++i) {
1106 impl_arg *vaarg = va->args->args[i];
1107 void *to_free = NULL;
1108 token_t let_fn, vatype = va->name->type->type;
1109
1110 if (vatype == PSI_T_MIXED) {
1111 switch (Z_TYPE_P(vaarg->_zv)) {
1112 case IS_TRUE:
1113 case IS_FALSE: vatype = PSI_T_BOOL; break;
1114 case IS_LONG: vatype = PSI_T_INT; break;
1115 case IS_DOUBLE: vatype = PSI_T_FLOAT; break;
1116 default: vatype = PSI_T_STRING; break;
1117 }
1118 }
1119
1120
1121 switch (vatype) {
1122 case PSI_T_BOOL: let_fn = PSI_T_BOOLVAL; break;
1123 case PSI_T_INT: let_fn = PSI_T_INTVAL; break;
1124 case PSI_T_FLOAT:
1125 case PSI_T_DOUBLE: let_fn = PSI_T_FLOATVAL;break;
1126 case PSI_T_STRING: let_fn = PSI_T_STRVAL; break;
1127 EMPTY_SWITCH_DEFAULT_CASE();
1128 }
1129
1130 va->types[i] = vatype;
1131 /* FIXME: varargs with struct-by-value :) */
1132 if (!psi_let_val(let_fn, vaarg, &va->values[i], NULL, &to_free)) {
1133 return NULL;
1134 }
1135
1136 if (to_free) {
1137 if (!va->free_list) {
1138 va->free_list = ecalloc(vacount - i + 1, sizeof(*va->free_list));
1139 }
1140 va->free_list[j++] = to_free;
1141 }
1142 }
1143
1144 return va;
1145 }
1146
1147 void psi_call(zend_execute_data *execute_data, zval *return_value, impl *impl)
1148 {
1149 size_t i;
1150 impl_vararg *va = NULL;
1151
1152 memset(impl->decl->func->ptr, 0, sizeof(impl_val));
1153
1154 if (SUCCESS != psi_parse_args(execute_data, impl)) {
1155 return;
1156 }
1157
1158 for (i = 0; i < impl->stmts->let.count; ++i) {
1159 let_stmt *let = impl->stmts->let.list[i];
1160
1161 if (!psi_do_let(let)) {
1162 psi_do_return(return_value, impl->stmts->ret.list[0]);
1163 psi_do_clean(impl);
1164 return;
1165 }
1166 }
1167
1168 if (impl->decl->args) {
1169 psi_do_args(impl);
1170
1171 if (impl->func->args->vararg.args) {
1172 va = psi_do_varargs(impl);
1173 }
1174 }
1175
1176 PSI_ContextCall(&PSI_G(context), &impl->decl->call, va);
1177 psi_do_return(return_value, impl->stmts->ret.list[0]);
1178
1179 for (i = 0; i < impl->stmts->set.count; ++i) {
1180 set_stmt *set = impl->stmts->set.list[i];
1181
1182 if (set->arg->_zv) {
1183 psi_do_set(set->arg->_zv, set->val);
1184 }
1185 }
1186
1187 for (i = 0; i < impl->stmts->fre.count; ++i) {
1188 free_stmt *fre = impl->stmts->fre.list[i];
1189
1190 psi_do_free(fre);
1191 }
1192 psi_do_clean(impl);
1193 }
1194
1195 static void psi_object_free(zend_object *o)
1196 {
1197 psi_object *obj = PSI_OBJ(NULL, o);
1198
1199 if (obj->data) {
1200 // free(obj->data);
1201 obj->data = NULL;
1202 }
1203 zend_object_std_dtor(o);
1204 }
1205
1206 static zend_object *psi_object_init(zend_class_entry *ce)
1207 {
1208 psi_object *o = ecalloc(1, sizeof(*o) + zend_object_properties_size(ce));
1209
1210 zend_object_std_init(&o->std, ce);
1211 object_properties_init(&o->std, ce);
1212 o->std.handlers = &psi_object_handlers;
1213 return &o->std;
1214 }
1215
1216 PHP_MINIT_FUNCTION(psi)
1217 {
1218 PSI_ContextOps *ops = NULL;
1219 zend_class_entry ce = {0};
1220
1221 REGISTER_INI_ENTRIES();
1222
1223 INIT_NS_CLASS_ENTRY(ce, "psi", "object", NULL);
1224 psi_class_entry = zend_register_internal_class_ex(&ce, NULL);
1225 psi_class_entry->create_object = psi_object_init;
1226
1227 memcpy(&psi_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
1228 psi_object_handlers.offset = XtOffsetOf(psi_object, std);
1229 psi_object_handlers.free_obj = psi_object_free;
1230 psi_object_handlers.clone_obj = NULL;
1231
1232 #ifdef HAVE_LIBJIT
1233 if (!strcasecmp(PSI_G(engine), "jit")) {
1234 ops = PSI_Libjit();
1235 } else
1236 #endif
1237 #ifdef HAVE_LIBFFI
1238 ops = PSI_Libffi();
1239 #endif
1240
1241 if (!ops) {
1242 php_error(E_WARNING, "No PSI engine found");
1243 return FAILURE;
1244 }
1245
1246 PSI_ContextInit(&PSI_G(context), ops, psi_error_wrapper);
1247 PSI_ContextBuild(&PSI_G(context), PSI_G(directory));
1248
1249 if (psi_check_env("PSI_DUMP")) {
1250 PSI_ContextDump(&PSI_G(context), STDOUT_FILENO);
1251 }
1252
1253 return SUCCESS;
1254 }
1255
1256 PHP_MSHUTDOWN_FUNCTION(psi)
1257 {
1258 PSI_ContextDtor(&PSI_G(context));
1259
1260 UNREGISTER_INI_ENTRIES();
1261
1262 return SUCCESS;
1263 }
1264
1265 #if defined(COMPILE_DL_PSI) && defined(ZTS)
1266 PHP_RINIT_FUNCTION(psi)
1267 {
1268 ZEND_TSRMLS_CACHE_UPDATE();
1269 return SUCCESS;
1270 }
1271 #endif
1272
1273 PHP_MINFO_FUNCTION(psi)
1274 {
1275 php_info_print_table_start();
1276 php_info_print_table_header(2, "psi support", "enabled");
1277 php_info_print_table_end();
1278
1279 DISPLAY_INI_ENTRIES();
1280 }
1281 const zend_function_entry psi_functions[] = {
1282 PHP_FE_END
1283 };
1284
1285 zend_module_entry psi_module_entry = {
1286 STANDARD_MODULE_HEADER,
1287 "psi",
1288 psi_functions,
1289 PHP_MINIT(psi),
1290 PHP_MSHUTDOWN(psi),
1291 #if defined(COMPILE_DL_PSI) && defined(ZTS)
1292 PHP_RINIT(psi), /* Replace with NULL if there's nothing to do at request start */
1293 #else
1294 NULL,
1295 #endif
1296 NULL,
1297 PHP_MINFO(psi),
1298 PHP_PSI_VERSION,
1299 STANDARD_MODULE_PROPERTIES
1300 };
1301
1302 #ifdef COMPILE_DL_PSI
1303 #ifdef ZTS
1304 ZEND_TSRMLS_CACHE_DEFINE();
1305 #endif
1306 ZEND_GET_MODULE(psi)
1307 #endif
1308
1309 /*
1310 * Local variables:
1311 * tab-width: 4
1312 * c-basic-offset: 4
1313 * End:
1314 * vim600: noet sw=4 ts=4 fdm=marker
1315 * vim<600: noet sw=4 ts=4
1316 */