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