flush
[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 #include "libffi.h"
15
16 ZEND_DECLARE_MODULE_GLOBALS(psi);
17
18 PHP_INI_BEGIN()
19 STD_PHP_INI_ENTRY("psi.engine", "ffi", PHP_INI_SYSTEM, OnUpdateString, engine, zend_psi_globals, psi_globals)
20 STD_PHP_INI_ENTRY("psi.directory", "psis", PHP_INI_SYSTEM, OnUpdateString, directory, zend_psi_globals, psi_globals)
21 PHP_INI_END();
22
23 void psi_error(int type, const char *msg, ...)
24 {
25 char buf[0x1000];
26 va_list argv;
27
28 va_start(argv, msg);
29 vslprintf(buf, 0x1000, msg, argv);
30 va_end(argv);
31
32 php_error(type, buf);
33 }
34
35 int psi_internal_type(impl_type *type)
36 {
37 switch (type->type) {
38 case PSI_T_BOOL:
39 return _IS_BOOL;
40 case PSI_T_INT:
41 return IS_LONG;
42 case PSI_T_FLOAT:
43 case PSI_T_DOUBLE:
44 return IS_DOUBLE;
45 case PSI_T_STRING:
46 return IS_STRING;
47 case PSI_T_ARRAY:
48 return IS_ARRAY;
49 default:
50 return 0;
51 }
52 }
53
54 zend_internal_arg_info *psi_internal_arginfo(impl *impl)
55 {
56 size_t i;
57 zend_internal_arg_info *aip;
58 zend_internal_function_info *fi;
59
60 aip = calloc(impl->func->args->count + 1, sizeof(*aip));
61
62 fi = (zend_internal_function_info *) &aip[0];
63 fi->required_num_args = psi_num_min_args(impl);
64 fi->return_reference = impl->func->return_reference;
65 fi->type_hint = psi_internal_type(impl->func->return_type);
66
67 for (i = 0; i < impl->func->args->count; ++i) {
68 impl_arg *iarg = impl->func->args->args[i];
69 zend_internal_arg_info *ai = &aip[i+1];
70
71 ai->name = iarg->var->name;
72 ai->type_hint = psi_internal_type(iarg->type);
73 if (iarg->var->reference) {
74 ai->pass_by_reference = 1;
75 }
76 if (iarg->var->reference || (iarg->def && iarg->def->type == PSI_T_NULL)) {
77 ai->allow_null = 1;
78 }
79 }
80
81 return aip;
82 }
83
84 size_t psi_num_min_args(impl *impl)
85 {
86 size_t i, n = impl->func->args->count;
87
88 for (i = 0; i < impl->func->args->count; ++i) {
89 if (impl->func->args->args[i]->def) {
90 --n;
91 }
92 }
93 return n;
94 }
95
96 void psi_to_bool(zval *return_value, token_t t, impl_val *ret_val, decl_var *var)
97 {
98 switch (t) {
99 case PSI_T_FLOAT:
100 RETVAL_DOUBLE((double) deref_impl_val(ret_val, var)->fval);
101 convert_to_boolean(return_value);
102 break;
103 case PSI_T_DOUBLE:
104 RETVAL_DOUBLE(deref_impl_val(ret_val, var)->dval);
105 convert_to_boolean(return_value);
106 break;
107 default:
108 RETVAL_LONG(deref_impl_val(ret_val, var)->lval);
109 convert_to_boolean(return_value);
110 break;
111 }
112 }
113
114 void psi_to_int(zval *return_value, token_t t, impl_val *ret_val, decl_var *var)
115 {
116 switch (t) {
117 case PSI_T_FLOAT:
118 RETVAL_DOUBLE((double) deref_impl_val(ret_val, var)->fval);
119 convert_to_long(return_value);
120 break;
121 case PSI_T_DOUBLE:
122 RETVAL_DOUBLE(deref_impl_val(ret_val, var)->dval);
123 convert_to_long(return_value);
124 break;
125 default:
126 RETVAL_LONG(deref_impl_val(ret_val, var)->lval);
127 }
128 }
129
130 void psi_to_double(zval *return_value, token_t t, impl_val *ret_val, decl_var *var)
131 {
132 switch (t) {
133 case PSI_T_FLOAT:
134 RETVAL_DOUBLE((double) deref_impl_val(ret_val, var)->fval);
135 break;
136 case PSI_T_DOUBLE:
137 RETVAL_DOUBLE(deref_impl_val(ret_val, var)->dval);
138 break;
139 default:
140 RETVAL_DOUBLE((double) deref_impl_val(ret_val, var)->lval);
141 break;
142 }
143 }
144
145 void psi_to_string(zval *return_value, token_t t, impl_val *ret_val, decl_var *var)
146 {
147 switch (t) {
148 case PSI_T_INT8:
149 case PSI_T_UINT8:
150 if (!var->arg->var->pointer_level) {
151 RETVAL_STRINGL(&ret_val->cval, 1);
152 } else {
153 ret_val = deref_impl_val(ret_val, var);
154 if (ret_val && ret_val->ptr) {
155 RETVAL_STRING(ret_val->ptr);
156 } else {
157 RETVAL_EMPTY_STRING();
158 }
159 }
160 break;
161 case PSI_T_FLOAT:
162 RETVAL_DOUBLE((double) deref_impl_val(ret_val, var)->fval);
163 convert_to_string(return_value);
164 break;
165 case PSI_T_DOUBLE:
166 RETVAL_DOUBLE(deref_impl_val(ret_val, var)->dval);
167 convert_to_string(return_value);
168 break;
169 default:
170 RETVAL_LONG(deref_impl_val(ret_val, var)->lval);
171 convert_to_string(return_value);
172 break;
173 }
174 }
175
176 size_t psi_t_alignment(token_t t)
177 {
178 size_t align;
179 #define PSI_TAS_D(T) struct PSI_TAS_ ##T { \
180 char c; \
181 T x; \
182 }
183 #define PSI_TAS_P(T) struct PSI_TAS_ ## T ## _pointer { \
184 char c; \
185 T *x; \
186 }
187 #define PSI_TAS_C(T) align = offsetof(struct PSI_TAS_ ##T, x)
188 #define PSI_TAS_CASE(T) { \
189 PSI_TAS_D(T); \
190 PSI_TAS_C(T); \
191 }
192 switch (t) {
193 case PSI_T_INT8:
194 PSI_TAS_CASE(int8_t);
195 break;
196 case PSI_T_UINT8:
197 PSI_TAS_CASE(uint8_t);
198 break;
199 case PSI_T_INT16:
200 PSI_TAS_CASE(int16_t);
201 break;
202 case PSI_T_UINT16:
203 PSI_TAS_CASE(uint16_t);
204 break;
205 case PSI_T_INT32:
206 PSI_TAS_CASE(int32_t);
207 break;
208 case PSI_T_UINT32:
209 PSI_TAS_CASE(uint32_t);
210 break;
211 case PSI_T_INT64:
212 PSI_TAS_CASE(int64_t);
213 break;
214 case PSI_T_UINT64:
215 PSI_TAS_CASE(uint64_t);
216 break;
217 case PSI_T_FLOAT:
218 PSI_TAS_CASE(float);
219 break;
220 case PSI_T_DOUBLE:
221 PSI_TAS_CASE(double);
222 break;
223 case PSI_T_POINTER:
224 {
225 PSI_TAS_P(char);
226 PSI_TAS_C(char_pointer);
227 }
228 break;
229 EMPTY_SWITCH_DEFAULT_CASE();
230 }
231
232 return align;
233 }
234
235 size_t psi_t_size(token_t t)
236 {
237 size_t size;
238
239 switch (t) {
240 case PSI_T_INT8:
241 case PSI_T_UINT8:
242 size = 1;
243 break;
244 case PSI_T_INT16:
245 case PSI_T_UINT16:
246 size = 2;
247 break;
248 case PSI_T_INT:
249 size = sizeof(int);
250 break;
251 case PSI_T_INT32:
252 case PSI_T_UINT32:
253 size = 4;
254 break;
255 case PSI_T_INT64:
256 case PSI_T_UINT64:
257 size = 8;
258 break;
259 case PSI_T_FLOAT:
260 size = sizeof(float);
261 break;
262 case PSI_T_DOUBLE:
263 size = sizeof(double);
264 break;
265 case PSI_T_POINTER:
266 size = sizeof(char *);
267 break;
268 EMPTY_SWITCH_DEFAULT_CASE();
269 }
270 return size;
271 }
272
273 size_t psi_t_align(token_t t, size_t s)
274 {
275 size_t a = psi_t_alignment(t);
276 return ((s - 1) | (a - 1)) + 1;
277 }
278
279 static impl_val *iterate(impl_val *val, token_t t, unsigned i, impl_val *tmp)
280 {
281 size_t size = psi_t_size(t);
282
283 memset(tmp, 0, sizeof(*tmp));
284 memcpy(tmp, val->ptr + size * i, size);
285 return tmp;
286 }
287
288 void psi_from_zval(impl_val *mem, decl_arg *spec, zval *zv, void **tmp)
289 {
290 decl_type *type = real_decl_type(spec->type);
291
292 switch (type->type) {
293 case PSI_T_FLOAT:
294 mem->fval = (float) zval_get_double(zv);
295 break;
296 case PSI_T_DOUBLE:
297 mem->dval = zval_get_double(zv);
298 break;
299 case PSI_T_INT8:
300 case PSI_T_UINT8:
301 if (spec->var->pointer_level) {
302 zend_string *zs = zval_get_string(zv);
303 *tmp = mem->ptr = estrndup(zs->val, zs->len);
304 zend_string_release(zs);
305 break;
306 }
307 /* no break */
308 default:
309 mem->zend.lval = zval_get_long(zv);
310 break;
311 }
312 }
313
314 void *psi_array_to_struct(decl_struct *s, HashTable *arr)
315 {
316 size_t i, j = 0;
317 char *mem = ecalloc(1, s->size + s->args->count * sizeof(void *));
318
319 if (arr) for (i = 0; i < s->args->count; ++i) {
320 decl_arg *darg = s->args->args[i];
321 zval *entry = zend_hash_str_find_ind(arr, darg->var->name, strlen(darg->var->name));
322
323 if (entry) {
324 impl_val val;
325 void *tmp = NULL;
326
327 memset(&tmp, 0, sizeof(tmp));
328 psi_from_zval(&val, darg, entry, &tmp);
329 memcpy(mem + darg->layout->pos, &val, darg->layout->len);
330 if (tmp) {
331 ((void **)(mem + s->size))[j++] = tmp;
332 }
333 }
334 }
335 return mem;
336 }
337
338 void psi_to_array(zval *return_value, token_t t, impl_val *ret_val, decl_var *var)
339 {
340 zval ele;
341 unsigned i;
342 impl_val tmp;
343
344 array_init(return_value);
345
346 if (t == PSI_T_STRUCT) {
347 decl_struct *s = real_decl_type(var->arg->type)->strct;
348 ret_val = deref_impl_val(ret_val, var);
349
350 ZEND_ASSERT(s);
351 for (i = 0; i < s->args->count; ++i) {
352 decl_arg *darg = s->args->args[i];
353 impl_val tmp, tmp_ptr;
354 zval ztmp;
355 char *ptr = (char *) ret_val->ptr + darg->layout->pos;
356
357 tmp_ptr.ptr = &tmp;
358 memset(&tmp, 0, sizeof(tmp));
359 memcpy(&tmp, ptr, darg->layout->len);
360 switch (real_decl_type(darg->type)->type) {
361 case PSI_T_FLOAT:
362 case PSI_T_DOUBLE:
363 psi_to_double(&ztmp, real_decl_type(darg->type)->type, &tmp, darg->var);
364 break;
365 case PSI_T_INT8:
366 case PSI_T_UINT8:
367 if (darg->var->pointer_level) {
368 psi_to_string(&ztmp, real_decl_type(darg->type)->type, &tmp_ptr, darg->var);
369 break;
370 }
371 /* no break */
372 case PSI_T_INT16:
373 case PSI_T_UINT16:
374 case PSI_T_INT32:
375 case PSI_T_UINT32:
376 case PSI_T_INT64:
377 case PSI_T_UINT64:
378 psi_to_int(&ztmp, real_decl_type(darg->type)->type, &tmp, darg->var);
379 break;
380 case PSI_T_STRUCT:
381 psi_to_array(&ztmp, real_decl_type(darg->type)->type, &tmp_ptr, darg->var);
382 break;
383 default:
384 printf("t=%d\n", real_decl_type(darg->type)->type);
385 abort();
386 }
387 add_assoc_zval(return_value, darg->var->name, &ztmp);
388 }
389 return;
390 }
391 ret_val = deref_impl_val(ret_val, var);
392 for (i = 0; i < var->arg->var->array_size; ++i) {
393 impl_val *ptr = iterate(ret_val, t, i, &tmp);
394
395 switch (t) {
396 case PSI_T_FLOAT:
397 ZVAL_DOUBLE(&ele, (double) ptr->fval);
398 break;
399 case PSI_T_DOUBLE:
400 ZVAL_DOUBLE(&ele, ptr->dval);
401 break;
402 default:
403 ZVAL_LONG(&ele, ptr->lval);
404 break;
405 }
406
407 add_next_index_zval(return_value, &ele);
408 }
409 }
410
411 ZEND_RESULT_CODE psi_parse_args(zend_execute_data *execute_data, impl *impl)
412 {
413 impl_arg *iarg;
414
415 if (!impl->func->args->count) {
416 return zend_parse_parameters_none();
417 }
418
419 ZEND_PARSE_PARAMETERS_START(psi_num_min_args(impl), impl->func->args->count)
420 nextarg:
421 iarg = impl->func->args->args[_i];
422 if (iarg->def) {
423 Z_PARAM_OPTIONAL;
424 }
425 if (PSI_T_BOOL == iarg->type->type) {
426 if (iarg->def) {
427 iarg->val.zend.bval = iarg->def->type == PSI_T_TRUE ? 1 : 0;
428 }
429 Z_PARAM_BOOL(iarg->val.zend.bval);
430 } else if (PSI_T_INT == iarg->type->type) {
431 if (iarg->def) {
432 iarg->val.zend.lval = zend_atol(iarg->def->text, strlen(iarg->def->text));
433 }
434 Z_PARAM_LONG(iarg->val.zend.lval);
435 } else if (PSI_T_FLOAT == iarg->type->type || PSI_T_DOUBLE == iarg->type->type) {
436 if (iarg->def) {
437 iarg->val.dval = zend_strtod(iarg->def->text, NULL);
438 }
439 Z_PARAM_DOUBLE(iarg->val.dval);
440 } else if (PSI_T_STRING == iarg->type->type) {
441 struct {char *val; size_t len;} str;
442 if (iarg->def) {
443 /* FIXME */
444 str.len = strlen(iarg->def->text) - 2;
445 str.val = &iarg->def->text[1];
446 }
447 Z_PARAM_STR_EX(iarg->val.zend.str, 1, 0);
448 if (iarg->val.zend.str) {
449 zend_string_addref(iarg->val.zend.str);
450 } else if (iarg->def) {
451 iarg->val.zend.str = zend_string_init(str.val, str.len, 0);
452 }
453 } else if (PSI_T_ARRAY == iarg->type->type) {
454 /* handled as _zv in let or set */
455 Z_PARAM_PROLOGUE(0);
456 } else {
457 error_code = ZPP_ERROR_FAILURE;
458 break;
459 }
460 iarg->_zv = _arg;
461 if (_i < _max_num_args) {
462 goto nextarg;
463 }
464 ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
465
466 return SUCCESS;
467 }
468
469 void *psi_do_calloc(let_calloc *alloc)
470 {
471 decl_type *type = real_decl_type(alloc->type);
472 size_t size;
473
474 if (type->type == PSI_T_STRUCT) {
475 /* psi_do_clean expects at least one NULL pointer after the struct */
476 size = type->strct->size + sizeof(void *);
477 } else {
478 size = psi_t_size(type->type);
479 }
480
481 return ecalloc(alloc->n, size);
482 }
483
484 void *psi_do_let(decl_arg *darg)
485 {
486 impl_arg *iarg = darg->let->arg;
487 impl_val *arg_val;
488
489 darg->let->ptr = &darg->let->out;
490 arg_val = darg->let->ptr;
491
492 if (!iarg) {
493 /* let foo = calloc(1, long);
494 * let foo = NULL;
495 * let foo;
496 */
497 if (darg->let->val->func && darg->let->val->func->type == PSI_T_CALLOC) {
498 arg_val->ptr = psi_do_calloc(darg->let->val->func->alloc);
499 darg->let->mem = arg_val->ptr;
500 } else if (darg->var->array_size) {
501 arg_val->ptr = ecalloc(darg->var->array_size, sizeof(*arg_val));
502 darg->let->mem = arg_val->ptr;
503 } else {
504 memset(arg_val, 0, sizeof(*arg_val));
505 }
506 } else {
507
508 switch (darg->let->val->func->type) {
509 case PSI_T_BOOLVAL:
510 if (iarg->type->type == PSI_T_BOOL) {
511 arg_val->cval = iarg->val.zend.bval;
512 } else {
513 arg_val->cval = zend_is_true(iarg->_zv);
514 }
515 break;
516 case PSI_T_INTVAL:
517 if (iarg->type->type == PSI_T_INT) {
518 arg_val->lval = iarg->val.zend.lval;
519 } else {
520 arg_val->lval = zval_get_long(iarg->_zv);
521 }
522 break;
523 case PSI_T_STRVAL:
524 if (iarg->type->type == PSI_T_STRING) {
525 arg_val->ptr = estrdup(iarg->val.zend.str->val);
526 darg->let->mem = arg_val->ptr;
527 zend_string_release(iarg->val.zend.str);
528 } else {
529 zend_string *zs = zval_get_string(iarg->_zv);
530 arg_val->ptr = estrdup(zs->val);
531 darg->let->mem = arg_val->ptr;
532 zend_string_release(zs);
533 }
534 break;
535 case PSI_T_STRLEN:
536 if (iarg->type->type == PSI_T_STRING) {
537 arg_val->lval = iarg->val.zend.str->len;
538 zend_string_release(iarg->val.zend.str);
539 } else {
540 zend_string *zs = zval_get_string(iarg->_zv);
541 arg_val->lval = zs->len;
542 zend_string_release(zs);
543 }
544 break;
545 case PSI_T_ARRVAL:
546 if (iarg->type->type == PSI_T_ARRAY) {
547 decl_type *type = real_decl_type(darg->type);
548
549 switch (type->type) {
550 case PSI_T_STRUCT:
551 arg_val->ptr = psi_array_to_struct(type->strct, HASH_OF(iarg->_zv));
552 darg->let->mem = arg_val->ptr;
553 break;
554 }
555 }
556 break;
557 EMPTY_SWITCH_DEFAULT_CASE();
558 }
559 }
560
561 if (darg->let->val && darg->let->val->is_reference) {
562 return &darg->let->ptr;
563 } else {
564 return darg->let->ptr;
565 }
566 }
567
568 void psi_do_set(zval *return_value, set_value *set)
569 {
570 impl_val *val = (impl_val *) &set->vars->vars[0]->arg->let->ptr;
571 token_t t = real_decl_type(set->vars->vars[0]->arg->type)->type;
572
573 ZVAL_DEREF(return_value);
574 zval_dtor(return_value);
575
576 set->func->handler(return_value, t, val, set->vars->vars[0]);
577 }
578
579 void psi_do_return(zval *return_value, return_stmt *ret, impl_val *ret_val)
580 {
581 token_t t = real_decl_type(ret->decl->type)->type;
582
583 ret->set->func->handler(return_value, t, ret_val, ret->decl->var);
584 }
585
586 void psi_do_free(free_stmt *fre)
587 {
588 size_t i;
589
590 for (i = 0; i < fre->vars->count; ++i) {
591 decl_var *dvar = fre->vars->vars[i];
592
593 if (dvar->arg && dvar->arg->let->out.ptr) {
594 free(dvar->arg->let->out.ptr);
595 dvar->arg->let->out.ptr = NULL;
596 }
597 }
598 }
599
600 void psi_do_clean(impl *impl)
601 {
602 size_t i;
603
604 for (i = 0; i < impl->func->args->count; ++i ) {
605 impl_arg *iarg = impl->func->args->args[i];
606
607 switch (iarg->type->type) {
608 case PSI_T_STRING:
609 if (iarg->val.zend.str) {
610 zend_string_release(iarg->val.zend.str);
611 }
612 break;
613 }
614 }
615
616 if (impl->decl->args) for (i = 0; i < impl->decl->args->count; ++i) {
617 decl_arg *darg = impl->decl->args->args[i];
618
619 if (darg->let && darg->let->mem) {
620 decl_type *type = real_decl_type(darg->type);
621
622 if (type->type == PSI_T_STRUCT) {
623 void **ptr = (void **) ((char *) darg->let->mem + type->strct->size);
624
625 while (*ptr) {
626 efree(*ptr++);
627 }
628 }
629 efree(darg->let->mem);
630 darg->let->mem = NULL;
631 }
632 }
633 }
634
635 PHP_MINIT_FUNCTION(psi)
636 {
637 PSI_ContextOps *ops;
638
639 REGISTER_INI_ENTRIES();
640
641 if (!strcasecmp(PSI_G(engine), "jit")) {
642 ops = PSI_Libjit();
643 } else {
644 ops = PSI_Libffi();
645 }
646
647 PSI_ContextInit(&PSI_G(context), ops, psi_error);
648 PSI_ContextBuild(&PSI_G(context), PSI_G(directory));
649
650 return SUCCESS;
651 }
652 PHP_MSHUTDOWN_FUNCTION(psi)
653 {
654 PSI_ContextDtor(&PSI_G(context));
655
656 UNREGISTER_INI_ENTRIES();
657
658 return SUCCESS;
659 }
660
661 /* Remove if there's nothing to do at request start */
662 /* {{{ PHP_RINIT_FUNCTION
663 */
664 PHP_RINIT_FUNCTION(psi)
665 {
666 #if defined(COMPILE_DL_PSI) && defined(ZTS)
667 ZEND_TSRMLS_CACHE_UPDATE();
668 #endif
669 return SUCCESS;
670 }
671 /* }}} */
672
673 /* Remove if there's nothing to do at request end */
674 /* {{{ PHP_RSHUTDOWN_FUNCTION
675 */
676 PHP_RSHUTDOWN_FUNCTION(psi)
677 {
678 return SUCCESS;
679 }
680 /* }}} */
681
682 PHP_MINFO_FUNCTION(psi)
683 {
684 php_info_print_table_start();
685 php_info_print_table_header(2, "psi support", "enabled");
686 php_info_print_table_end();
687
688 DISPLAY_INI_ENTRIES();
689 }
690 const zend_function_entry psi_functions[] = {
691 PHP_FE_END
692 };
693
694 zend_module_entry psi_module_entry = {
695 STANDARD_MODULE_HEADER,
696 "psi",
697 psi_functions,
698 PHP_MINIT(psi),
699 PHP_MSHUTDOWN(psi),
700 PHP_RINIT(psi), /* Replace with NULL if there's nothing to do at request start */
701 PHP_RSHUTDOWN(psi), /* Replace with NULL if there's nothing to do at request end */
702 PHP_MINFO(psi),
703 PHP_PSI_VERSION,
704 STANDARD_MODULE_PROPERTIES
705 };
706
707 #ifdef COMPILE_DL_PSI
708 #ifdef ZTS
709 ZEND_TSRMLS_CACHE_DEFINE();
710 #endif
711 ZEND_GET_MODULE(psi)
712 #endif
713
714 /*
715 * Local variables:
716 * tab-width: 4
717 * c-basic-offset: 4
718 * End:
719 * vim600: noet sw=4 ts=4 fdm=marker
720 * vim<600: noet sw=4 ts=4
721 */