Merge branch 'callbacks'
authorMichael Wallner <mike@php.net>
Fri, 12 Feb 2016 15:01:07 +0000 (16:01 +0100)
committerMichael Wallner <mike@php.net>
Fri, 12 Feb 2016 15:01:07 +0000 (16:01 +0100)
17 files changed:
.gitattributes [new file with mode: 0644]
TODO
src/context.c
src/context_dump.c
src/context_validate.c
src/engine.c
src/engine.h
src/libffi.c
src/marshal.c
src/marshal.h
src/parser.h
src/parser.re
src/parser_proc.h
src/parser_proc.y
tests/sqlite/sqlite.psi [new file with mode: 0644]
tests/sqlite/sqlite001.db [new file with mode: 0644]
tests/sqlite/sqlite001.phpt [new file with mode: 0644]

diff --git a/.gitattributes b/.gitattributes
new file mode 100644 (file)
index 0000000..375cd8c
--- /dev/null
@@ -0,0 +1 @@
+/tests/sqlite/sqlite001.db binary
diff --git a/TODO b/TODO
index ee63abe58cf690118489fa11ee906b9ec94ee5c8..ffda07aae8dbe8c7289a913facdc78b3d176933b 100644 (file)
--- a/TODO
+++ b/TODO
@@ -6,3 +6,4 @@
 * pemalloc
 * unions
 * callbacks and function pointers
+* let impl_Var point to impl_arg, just like decl_var?
\ No newline at end of file
index ed26f8223214fb62c989e5ee8b57170a75771476..c86a595d8c79be86b07db026024c476080d3adcf 100644 (file)
@@ -342,6 +342,12 @@ void PSI_ContextDtor(PSI_Context *C)
                }
                free(C->structs);
        }
+       if (C->unions) {
+               if (C->unions->list) {
+                       free(C->unions->list);
+               }
+               free(C->unions);
+       }
        if (C->enums) {
                if (C->enums->list) {
                        free(C->enums->list);
index b778525c93a4f4aa76bf9df59a2240933da73036..8e11eca41763589155de8a03fed85780aa463a01 100644 (file)
@@ -127,10 +127,10 @@ static inline void dump_impl_set_value(int fd, set_value *set, unsigned level, i
                dprintf(fd, ", ");
                dump_num_exp(fd, set->num);
        }
-       if (set->inner && set->func->type != PSI_T_ELLIPSIS) {
+       if (set->inner && set->inner->vals && set->func->type != PSI_T_ELLIPSIS) {
                dprintf(fd, ",\n");
-               for (i = 0; i < set->count; ++i) {
-                       dump_impl_set_value(fd, set->inner[i], level+1, i == (set->count - 1));
+               for (i = 0; i < set->inner->count; ++i) {
+                       dump_impl_set_value(fd, set->inner->vals[i], level+1, i == (set->inner->count - 1));
                }
                /* only if inner stmts, i.e. with new lines, were dumped */
                dump_level(fd, level);
index 45e2d987efba15185e435cae228ae99b3fef9d3d..180b34d94c3e3b652f7e20e1184a4bbe13019020 100644 (file)
@@ -13,6 +13,7 @@
 #include "php_psi_macros.h"
 #include "php_psi_redirs.h"
 
+#include "parser.h"
 #include "calc.h"
 #include "marshal.h"
 #include "engine.h"
@@ -111,6 +112,22 @@ static inline int locate_decl_type_enum(decl_enums *enums, decl_type *type) {
        return 0;
 }
 
+static inline int locate_decl_type_decl(decls *decls, decl_type *type) {
+       size_t i;
+
+       if (type->func) {
+               return 1;
+       }
+       if (decls) for (i = 0; i < decls->count; ++i) {
+               if (!strcmp(decls->list[i]->func->var->name, type->name)) {
+                       type->func = decls->list[i];
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
 static inline int validate_decl_struct(PSI_Data *data, decl_struct *s);
 static inline int validate_decl_union(PSI_Data *data, decl_union *u);
 static inline int validate_decl_enum(PSI_Data *data, decl_enum *e);
@@ -143,6 +160,12 @@ static inline int validate_decl_type(PSI_Data *data, decl_type *type) {
                if (!locate_decl_type_enum(data->enums, type)) {
                        return 0;
                }
+               break;
+       case PSI_T_FUNCTION:
+               if (!locate_decl_type_decl(data->decls, type)) {
+                       return 0;
+               }
+               break;
        }
        return 1;
 }
@@ -508,9 +531,6 @@ static inline int validate_decl_func(PSI_Data *data, void *dl, decl *decl, decl_
                return 0;
        }
 
-       if (!validate_decl_arg(data, func)) {
-               return 0;
-       }
        for (redir = &psi_func_redirs[0]; redir->name; ++redir) {
                if (!strcmp(func->var->name, redir->name)) {
                        decl->call.sym = redir->func;
@@ -529,14 +549,13 @@ static inline int validate_decl_func(PSI_Data *data, void *dl, decl *decl, decl_
        }
        return 1;
 }
-
-static inline int validate_decl(PSI_Data *data, void *dl, decl *decl) {
+static inline int validate_decl_nodl(PSI_Data *data, decl *decl) {
        if (!validate_decl_abi(data, decl->abi)) {
                data->error(data, decl->abi->token, PSI_WARNING,
                                "Invalid calling convention: '%s'", decl->abi->token->text);
                return 0;
        }
-       if (!validate_decl_func(data, dl, decl, decl->func)) {
+       if (!validate_decl_arg(data, decl->func)) {
                return 0;
        }
        if (decl->args) {
@@ -550,6 +569,15 @@ static inline int validate_decl(PSI_Data *data, void *dl, decl *decl) {
        }
        return 1;
 }
+static inline int validate_decl(PSI_Data *data, void *dl, decl *decl) {
+       if (!validate_decl_nodl(data, decl)) {
+               return 0;
+       }
+       if (!validate_decl_func(data, dl, decl, decl->func)) {
+               return 0;
+       }
+       return 1;
+}
 static inline decl_arg *locate_decl_var_arg(decl_var *var, decl_args *args, decl_arg *func) {
        size_t i;
 
@@ -696,32 +724,18 @@ static inline int validate_decl_enum(PSI_Data *data, decl_enum *e) {
 
 static inline int validate_set_value_handler(set_value *set) {
        switch (set->func->type) {
-       case PSI_T_TO_BOOL:
-               set->func->handler = psi_to_bool;
-               break;
-       case PSI_T_TO_INT:
-               set->func->handler = psi_to_int;
-               break;
-       case PSI_T_TO_FLOAT:
-               set->func->handler = psi_to_double;
-               break;
-       case PSI_T_TO_STRING:
-               set->func->handler = psi_to_string;
-               break;
-       case PSI_T_TO_ARRAY:
-               set->func->handler = psi_to_array;
-               break;
-       case PSI_T_TO_OBJECT:
-               set->func->handler = psi_to_object;
-               break;
-       case PSI_T_VOID:
-               set->func->handler = psi_to_void;
-               break;
+       case PSI_T_TO_BOOL:             set->func->handler = psi_to_bool;               break;
+       case PSI_T_TO_INT:              set->func->handler = psi_to_int;                break;
+       case PSI_T_TO_FLOAT:    set->func->handler = psi_to_double;             break;
+       case PSI_T_TO_STRING:   set->func->handler = psi_to_string;             break;
+       case PSI_T_TO_ARRAY:    set->func->handler = psi_to_array;              break;
+       case PSI_T_TO_OBJECT:   set->func->handler = psi_to_object;             break;
+       case PSI_T_VOID:                set->func->handler = psi_to_void;               break;
+       case PSI_T_ZVAL:                set->func->handler = psi_to_zval;               break;
        case PSI_T_ELLIPSIS:
                if (set->outer.set && set->outer.set->func->type == PSI_T_TO_ARRAY) {
                        set->func->handler = psi_to_recursive;
                        set->inner = set->outer.set->inner;
-                       set->count = set->outer.set->count;
                        break;
                }
                /* no break */
@@ -766,7 +780,7 @@ static inline int validate_set_value_ex(PSI_Data *data, set_value *set, decl_arg
        }
        ref_type = real_decl_type(ref->type);
 
-       if (set->count) {
+       if (set->inner && set->inner->count) {
                int is_to_array = (set->func->type == PSI_T_TO_ARRAY);
                int is_pointer_to_struct = (ref_type->type == PSI_T_STRUCT && ref->var->pointer_level);
 
@@ -782,23 +796,23 @@ static inline int validate_set_value_ex(PSI_Data *data, set_value *set, decl_arg
                }
        }
 
-       if (ref_type->type == PSI_T_STRUCT) {
+       if (set->inner && ref_type->type == PSI_T_STRUCT) {
                /* to_array(struct, to_...) */
-               if (!set->outer.set || set->outer.set->inner != set->inner) {
-                       for (i = 0; i < set->count; ++i) {
-                               decl_var *sub_var = set->inner[i]->vars->vars[0];
+               if (!set->outer.set || set->outer.set->inner->vals != set->inner->vals) {
+                       for (i = 0; i < set->inner->count; ++i) {
+                               decl_var *sub_var = set->inner->vals[i]->vars->vars[0];
                                decl_arg *sub_ref = locate_struct_member(ref_type->strct, sub_var);
 
                                if (sub_ref) {
-                                       if (!validate_set_value_ex(data, set->inner[i], sub_ref, ref_type->strct->args)) {
+                                       if (!validate_set_value_ex(data, set->inner->vals[i], sub_ref, ref_type->strct->args)) {
                                                return 0;
                                        }
                                }
                        }
                }
-       } else if (set->count == 1) {
+       } else if (set->inner && set->inner->count == 1) {
                /* to_array(ptr, to_string(*ptr)) */
-               decl_var *sub_var = set->inner[0]->vars->vars[0];
+               decl_var *sub_var = set->inner->vals[0]->vars->vars[0];
                decl_arg *sub_ref = locate_decl_var_arg(sub_var, ref_list, ref);
 
                if (sub_ref) {
@@ -806,11 +820,11 @@ static inline int validate_set_value_ex(PSI_Data *data, set_value *set, decl_arg
                                data->error(data, sub_var->token, E_WARNING, "Inner `set` statement casts on pointers must reference the same variable");
                                return 0;
                        }
-                       if (!validate_set_value_ex(data, set->inner[0], sub_ref, ref_list)) {
+                       if (!validate_set_value_ex(data, set->inner->vals[0], sub_ref, ref_list)) {
                                return 0;
                        }
                }
-       } else if (set->count > 1) {
+       } else if (set->inner && set->inner->count > 1) {
                data->error(data, set->func->token, E_WARNING, "Inner `set` statement casts on pointers may only occur once");
                return 0;
        }
@@ -878,7 +892,75 @@ static inline int validate_impl_ret_stmt(PSI_Data *data, impl *impl) {
                return 0;
        }
 
-       impl->decl->impl = impl;
+       //impl->decl->impl = impl;
+
+       return 1;
+}
+
+static inline impl_arg *locate_impl_var_arg(impl_var *var, impl_args *args) {
+       size_t i;
+
+       for (i = 0; i < args->count; ++i) {
+               impl_arg *iarg = args->args[i];
+
+               if (!strcmp(var->name, iarg->var->name)) {
+                       return var->arg = iarg;
+               }
+       }
+
+       return NULL;
+}
+
+static inline int validate_let_func(PSI_Data *data, let_func *func, impl *impl) {
+       if (impl->func->args) {
+               if (!locate_impl_var_arg(func->var, impl->func->args)) {
+                       data->error(data, func->var->token, PSI_WARNING,
+                                       "Unknown variable '$%s' of `let` statement"
+                                       " for cast '%s' of implementation '%s'",
+                                       func->var->name, func->name, impl->func->name);
+                       return 0;
+               }
+       }
+       switch (func->type) {
+       case PSI_T_BOOLVAL:             func->handler = psi_let_boolval;        break;
+       case PSI_T_INTVAL:              func->handler = psi_let_intval;         break;
+       case PSI_T_FLOATVAL:    func->handler = psi_let_floatval;       break;
+       case PSI_T_STRVAL:              func->handler = psi_let_strval;         break;
+       case PSI_T_STRLEN:              func->handler = psi_let_strlen;         break;
+       case PSI_T_PATHVAL:             func->handler = psi_let_pathval;        break;
+       case PSI_T_ARRVAL:              func->handler = psi_let_arrval;         break;
+       case PSI_T_OBJVAL:              func->handler = psi_let_objval;         break;
+       case PSI_T_ZVAL:                func->handler = psi_let_zval;           break;
+       EMPTY_SWITCH_DEFAULT_CASE();
+       }
+       return 1;
+}
+
+static inline int validate_let_callback(PSI_Data *data, decl_var *cb_var, let_callback *cb, impl *impl) {
+       size_t i;
+       decl *cb_func;
+       decl_type *cb_type = real_decl_type(cb_var->arg->type);
+
+       if (!validate_let_func(data, cb->func, impl)) {
+               return 0;
+       }
+
+       if (cb_type->type != PSI_T_FUNCTION) {
+               data->error(data, cb_var->token, PSI_WARNING, "Not a function: %s", cb_var->name);
+               return 0;
+       }
+       cb_func = cb_type->func;
+       for (i = 0; i < cb->args->count; ++i) {
+               if (!validate_set_value(data, cb->args->vals[i], cb_func->args->count, cb_func->args->args, 0)) {
+                       return 0;
+               }
+       }
+
+       if (!validate_decl_nodl(data, cb_func)) {
+               return 0;
+       }
+
+       cb->decl = cb_func;
 
        return 1;
 }
@@ -891,7 +973,6 @@ static inline int validate_impl_let_stmts(PSI_Data *data, impl *impl) {
        for (i = 0; i < impl->stmts->let.count; ++i) {
                let_stmt *let = impl->stmts->let.list[i];
                decl_var *let_var;
-               int check = 0;
 
                if (let->val && let->val->kind == PSI_LET_TMP) {
                        let_var = let->val->data.var;
@@ -934,22 +1015,13 @@ static inline int validate_impl_let_stmts(PSI_Data *data, impl *impl) {
                                return 0;
                        }
                        break;
-               case PSI_LET_FUNC:
-                       if (impl->func->args) {
-                               for (j = 0; j < impl->func->args->count; ++j) {
-                                       impl_arg *iarg = impl->func->args->args[j];
-
-                                       if (!strcmp(let->val->data.func->var->name, iarg->var->name)) {
-                                               let->val->data.func->arg = iarg;
-                                               check = 1;
-                                               break;
-                                       }
-                               }
+               case PSI_LET_CALLBACK:
+                       if (!validate_let_callback(data, let->var, let->val->data.callback, impl)) {
+                               return 0;
                        }
-                       if (!check) {
-                               data->error(data, let->var->token, PSI_WARNING, "Unknown value '$%s' of `let` statement"
-                                               " for variable '%s' of implementation '%s'",
-                                               let->val->data.func->var->name, let->var->name, impl->func->name);
+                       break;
+               case PSI_LET_FUNC:
+                       if (!validate_let_func(data, let->val->data.func, impl)) {
                                return 0;
                        }
                        break;
index dc9d2746f235aff80c90a36a35eaf7319dbf0488..4415e4b093dc4c656f491d19798fa40754074e1f 100644 (file)
@@ -7,6 +7,7 @@
 
 #include "zend_exceptions.h"
 
+#include "parser.h"
 #include "engine.h"
 #include "calc.h"
 #include "marshal.h"
@@ -193,6 +194,17 @@ static inline ZEND_RESULT_CODE psi_parse_args(zend_execute_data *execute_data, i
                        Z_PARAM_PROLOGUE(0);
                } else if (PSI_T_MIXED == iarg->type->type) {
                        Z_PARAM_PROLOGUE(0);
+               } else if (PSI_T_CALLABLE == iarg->type->type) {
+                       zend_fcall_info fci;
+                       zend_fcall_info_cache fcc;
+
+                       Z_PARAM_FUNC_EX(fci, fcc, 1, 0);
+
+                       if (fci.size) {
+                               iarg->val.zend.cb = ecalloc(1, sizeof(zend_fcall));
+                               iarg->val.zend.cb->fci = fci;
+                               iarg->val.zend.cb->fcc = fcc;
+                       }
                } else {
                        error_code = ZPP_ERROR_FAILURE;
                        break;
@@ -248,91 +260,18 @@ static inline void *psi_do_calloc(let_calloc *alloc)
 
 static inline impl_val *psi_let_val(token_t let_func, impl_arg *iarg, impl_val *arg_val, decl_struct *strct, void **to_free)
 {
-       switch (let_func) {
-       case PSI_T_BOOLVAL:
-               if (iarg->type->type == PSI_T_BOOL) {
-                       arg_val->cval = iarg->val.zend.bval;
-               } else {
-                       arg_val->cval = zend_is_true(iarg->_zv);
-               }
-               break;
-       case PSI_T_INTVAL:
-               if (iarg->type->type == PSI_T_INT) {
-                       arg_val->lval = iarg->val.zend.lval;
-               } else {
-                       arg_val->lval = zval_get_long(iarg->_zv);
-               }
-               break;
-       case PSI_T_FLOATVAL:
-               if (iarg->type->type == PSI_T_FLOAT || iarg->type->type == PSI_T_DOUBLE) {
-                       arg_val->dval = iarg->val.dval;
-               } else {
-                       arg_val->dval = zval_get_double(iarg->_zv);
-               }
-               break;
-       case PSI_T_PATHVAL:
-       case PSI_T_STRVAL:
-               if (iarg->type->type == PSI_T_STRING) {
-                       if (iarg->val.zend.str) {
-                               arg_val->ptr = estrndup(iarg->val.zend.str->val, iarg->val.zend.str->len);
-                               *to_free = arg_val->ptr;
-                       } else {
-                               arg_val->ptr = "";
-                       }
-               } else {
-                       zend_string *zs = zval_get_string(iarg->_zv);
-                       arg_val->ptr = estrdup(zs->val);
-                       *to_free = arg_val->ptr;
-                       zend_string_release(zs);
-               }
-               if (PSI_T_PATHVAL == let_func) {
-                       if (SUCCESS != php_check_open_basedir(arg_val->ptr)) {
-                               efree(arg_val->ptr);
-                               return NULL;
-                       }
-               }
-               break;
-       case PSI_T_STRLEN:
-               if (iarg->type->type == PSI_T_STRING) {
-                       if (iarg->val.zend.str) {
-                               arg_val->lval = iarg->val.zend.str->len;
-                       } else {
-                               arg_val->lval = 0;
-                       }
-               } else {
-                       zend_string *zs = zval_get_string(iarg->_zv);
-                       arg_val->lval = zs->len;
-                       zend_string_release(zs);
-               }
-               break;
-       case PSI_T_ARRVAL:
-               if (iarg->type->type == PSI_T_ARRAY) {
-                       arg_val = psi_array_to_struct(strct, HASH_OF(iarg->_zv));
-                       *to_free = arg_val;
-               }
-               break;
-       case PSI_T_OBJVAL:
-               if (iarg->type->type == PSI_T_OBJECT) {
-                       psi_object *obj;
-
-                       if (!instanceof_function(Z_OBJCE_P(iarg->_zv), psi_object_get_class_entry())) {
-                               return NULL;
-                       }
-
-                       obj = PSI_OBJ(iarg->_zv, NULL);
-                       arg_val->ptr = obj->data;
-               }
-               break;
-       EMPTY_SWITCH_DEFAULT_CASE();
-       }
+       abort();
        return arg_val;
 }
 
+static inline impl_val *psi_let_func(let_func *func, decl_arg *darg) {
+       return darg->ptr = func->handler(darg->ptr, darg->type, func->var->arg, &darg->mem);
+}
+
 static inline void *psi_do_let(let_stmt *let)
 {
        decl_arg *darg = let->var->arg;
        impl_val *arg_val = darg->ptr;
-       impl_arg *iarg;
 
        switch (let->val ? let->val->kind : PSI_LET_NULL) {
        case PSI_LET_TMP:
@@ -355,15 +294,17 @@ static inline void *psi_do_let(let_stmt *let)
                arg_val->ptr = psi_do_calloc(let->val->data.alloc);
                darg->mem = arg_val->ptr;
                break;
+       case PSI_LET_CALLBACK:
+               arg_val->ptr = let->val->data.callback->decl->call.sym;
+               break;
        case PSI_LET_NUMEXP:
                arg_val->zend.lval = psi_long_num_exp(let->val->data.num, NULL);
                break;
        case PSI_LET_FUNC:
-               iarg = let->val->data.func->arg;
-
-               if (!(darg->ptr = psi_let_val(let->val->data.func->type, iarg, darg->ptr, real_decl_type(darg->type)->strct, &darg->mem))) {
+               if (!psi_let_func(let->val->data.func, darg)) {
                        return NULL;
                }
+               break;
        }
 
        if (let->val && let->val->flags.one.is_reference) {
@@ -373,14 +314,6 @@ static inline void *psi_do_let(let_stmt *let)
        }
 }
 
-static inline void psi_do_set(zval *return_value, set_value *set)
-{
-       decl_arg *set_arg = set->vars->vars[0]->arg;
-
-       zval_dtor(return_value);
-       set->func->handler(return_value, set, set_arg->let ? set_arg->let->ptr : set_arg->ptr);
-}
-
 static inline void psi_do_return(zval *return_value, return_stmt *ret)
 {
        ret->set->func->handler(return_value, ret->set, ret->set->vars->vars[0]->arg->ptr);
@@ -439,6 +372,14 @@ static inline void psi_do_clean(impl *impl)
                                zend_string_release(iarg->val.zend.str);
                        }
                        break;
+               case PSI_T_CALLABLE:
+                       if (iarg->val.zend.cb) {
+                               if (iarg->val.zend.cb->fci.size) {
+                                       zend_fcall_info_args_clear(&iarg->val.zend.cb->fci, 1);
+                               }
+                               efree(iarg->val.zend.cb);
+                       }
+                       break;
                }
        }
 
index 364ec550fd760350e5d334522b842b8ab6a7f63f..83f64f14b6507b0519a473c1183354cc98b30629 100644 (file)
@@ -16,6 +16,14 @@ static inline size_t psi_offset_padding(size_t diff, size_t alignment) {
        return diff;
 }
 
+static inline void psi_do_set(zval *return_value, set_value *set)
+{
+       decl_arg *set_arg = set->vars->vars[0]->arg;
+
+       zval_dtor(return_value);
+       set->func->handler(return_value, set, set_arg->let ? set_arg->let->ptr : set_arg->ptr);
+}
+
 int psi_internal_type(impl_type *type);
 zend_internal_arg_info *psi_internal_arginfo(impl *impl);
 size_t psi_num_min_args(impl *impl);
index be9edfd26ec1cb4a5df3a7210fc3a5b94f482907..514394f4047f506650473e367a724a7c729d4d8b 100644 (file)
@@ -43,8 +43,23 @@ static void *psi_ffi_closure_alloc(size_t s, void **code)
        }
        return *code;
 #else
-       return NULL;
+# error "Neither ffi_closure_alloc() nor mmap() available"
+#endif
+}
+
+static ffi_status psi_ffi_prep_closure(ffi_closure **closure, void **code, ffi_cif *sig, void (*handler)(ffi_cif*,void*,void**,void*), void *data) {
+       *closure = psi_ffi_closure_alloc(sizeof(ffi_closure), code);
+       ZEND_ASSERT(*closure != NULL);
+
+#if PSI_HAVE_FFI_PREP_CLOSURE_LOC
+       return ffi_prep_closure_loc(*closure, sig, handler, data, *code);
+
+#elif PSI_HAVE_FFI_PREP_CLOSURE
+       return ffi_prep_closure(*code, sig, handler, data);
+#else
+# error "Neither ffi_prep_closure() nor ffi_prep_closure_loc() is available"
 #endif
+
 }
 
 static void psi_ffi_closure_free(void *c)
@@ -56,12 +71,116 @@ static void psi_ffi_closure_free(void *c)
 #endif
 }
 
-static void psi_ffi_handler(ffi_cif *signature, void *_result, void **_args, void *_data);
+static void psi_ffi_handler(ffi_cif *_sig, void *_result, void **_args, void *_data)
+{
+       psi_call(*(zend_execute_data **)_args[0], *(zval **)_args[1], _data);
+}
+
+static void psi_ffi_callback(ffi_cif *_sig, void *_result, void **_args, void *_data)
+{
+       size_t i;
+       unsigned argc = _sig->nargs;
+       void **argv = _args;
+       let_callback *cb = _data;
+       decl *decl_cb = cb->decl;
+       impl_arg *iarg = cb->func->var->arg;
+       zval return_value, *zargv = calloc(argc, sizeof(*zargv));
+       void *result, *to_free = NULL;
+
+       ZEND_ASSERT(argc == cb->decl->args->count);
+
+       /* prepare args for the userland call */
+       for (i = 0; i < argc; ++i) {
+               cb->decl->args->args[i]->ptr = argv[i];
+       }
+       for (i = 0; i < cb->args->count; ++i) {
+               psi_do_set(&zargv[i], cb->args->vals[i]);
+       }
+       zend_fcall_info_argp(&iarg->val.zend.cb->fci, cb->args->count, zargv);
+
+       /* callback into userland */
+       ZVAL_UNDEF(&return_value);
+       iarg->_zv = &return_value;
+       zend_fcall_info_call(&iarg->val.zend.cb->fci, &iarg->val.zend.cb->fcc, iarg->_zv, NULL);
+
+       /* marshal return value of the userland call */
+       switch (iarg->type->type) {
+       case PSI_T_BOOL:        zend_parse_arg_bool(iarg->_zv, &iarg->val.zend.bval, NULL, 0);          break;
+       case PSI_T_LONG:        zend_parse_arg_long(iarg->_zv, &iarg->val.zend.lval, NULL, 0, 1);       break;
+       case PSI_T_FLOAT:
+       case PSI_T_DOUBLE:      zend_parse_arg_double(iarg->_zv, &iarg->val.dval, NULL, 0);                     break;
+       case PSI_T_STRING:      zend_parse_arg_str(iarg->_zv, &iarg->val.zend.str, 0);                          break;
+       }
+       result = cb->func->handler(_result, decl_cb->func->type, iarg, &to_free);
+
+       if (result != _result) {
+               *(void **)_result = result;
+       }
+
+       zend_fcall_info_args_clear(&iarg->val.zend.cb->fci, 0);
+       for (i = 0; i < cb->args->count; ++i) {
+               zval_ptr_dtor(&zargv[i]);
+       }
+       free(zargv);
+}
+
 static inline ffi_type *psi_ffi_decl_arg_type(decl_arg *darg);
 
+typedef struct PSI_LibffiContext {
+       ffi_cif signature;
+       ffi_type *params[2];
+} PSI_LibffiContext;
+
+typedef struct PSI_LibffiCall {
+       void *code;
+       ffi_closure *closure;
+       ffi_cif signature;
+       void *params[1]; /* [type1, type2, NULL, arg1, arg2] ... */
+} PSI_LibffiCall;
+
 static inline ffi_abi psi_ffi_abi(const char *convention) {
        return FFI_DEFAULT_ABI;
 }
+
+static inline PSI_LibffiCall *PSI_LibffiCallAlloc(PSI_Context *C, decl *decl) {
+       int rc;
+       size_t i, c = decl->args ? decl->args->count : 0;
+       PSI_LibffiCall *call = calloc(1, sizeof(*call) + 2 * c * sizeof(void *));
+
+       for (i = 0; i < c; ++i) {
+               call->params[i] = psi_ffi_decl_arg_type(decl->args->args[i]);
+       }
+       call->params[c] = NULL;
+
+       decl->call.info = call;
+       decl->call.rval = &decl->func->ptr;
+       decl->call.argc = c;
+       decl->call.args = (void **) &call->params[c+1];
+
+       rc = ffi_prep_cif(&call->signature, psi_ffi_abi(decl->abi->convention),
+                       c, psi_ffi_decl_arg_type(decl->func), (ffi_type **) call->params);
+       ZEND_ASSERT(FFI_OK == rc);
+
+       return call;
+}
+
+static inline ffi_status PSI_LibffiCallInitClosure(PSI_Context *C, PSI_LibffiCall *call, impl *impl) {
+       PSI_LibffiContext *context = C->context;
+
+       return psi_ffi_prep_closure(&call->closure, &call->code, &context->signature, psi_ffi_handler, impl);
+}
+
+static inline ffi_status PSI_LibffiCallInitCallbackClosure(PSI_Context *C, PSI_LibffiCall *call, let_callback *cb) {
+       return psi_ffi_prep_closure(&call->closure, &call->code, &call->signature, psi_ffi_callback, cb);
+}
+
+static inline void PSI_LibffiCallFree(PSI_LibffiCall *call) {
+       if (call->closure) {
+               psi_ffi_closure_free(call->closure);
+       }
+       free(call);
+}
+
 static inline ffi_type *psi_ffi_token_type(token_t t) {
        switch (t) {
        default:
@@ -194,6 +313,9 @@ static inline ffi_type *psi_ffi_decl_type(decl_type *type) {
        decl_type *real = real_decl_type(type);
 
        switch (real->type) {
+       case PSI_T_FUNCTION:
+               return &ffi_type_pointer;
+
        case PSI_T_STRUCT:
                if (!real->strct->engine.type) {
                        ffi_type *strct = calloc(1, sizeof(ffi_type));
@@ -223,69 +345,6 @@ static inline ffi_type *psi_ffi_decl_arg_type(decl_arg *darg) {
        }
 }
 
-typedef struct PSI_LibffiContext {
-       ffi_cif signature;
-       ffi_type *params[2];
-} PSI_LibffiContext;
-
-typedef struct PSI_LibffiCall {
-       void *code;
-       ffi_closure *closure;
-       ffi_cif signature;
-       void *params[1]; /* [type1, type2, NULL, arg1, arg2] ... */
-} PSI_LibffiCall;
-
-static inline PSI_LibffiCall *PSI_LibffiCallAlloc(PSI_Context *C, decl *decl) {
-       int rc;
-       size_t i, c = decl->args ? decl->args->count : 0;
-       PSI_LibffiCall *call = calloc(1, sizeof(*call) + 2 * c * sizeof(void *));
-
-       for (i = 0; i < c; ++i) {
-               call->params[i] = psi_ffi_decl_arg_type(decl->args->args[i]);
-       }
-       call->params[c] = NULL;
-
-       decl->call.info = call;
-       decl->call.rval = &decl->func->ptr;
-       decl->call.argc = c;
-       decl->call.args = (void **) &call->params[c+1];
-
-       rc = ffi_prep_cif(&call->signature, psi_ffi_abi(decl->abi->convention),
-                       c, psi_ffi_decl_arg_type(decl->func), (ffi_type **) call->params);
-       ZEND_ASSERT(FFI_OK == rc);
-
-       return call;
-}
-
-static inline void PSI_LibffiCallInitClosure(PSI_Context *C, PSI_LibffiCall *call, impl *impl) {
-       PSI_LibffiContext *context = C->context;
-       int rc;
-
-       call->closure = psi_ffi_closure_alloc(sizeof(ffi_closure), &call->code);
-       ZEND_ASSERT(call->closure != NULL);
-
-#if PSI_HAVE_FFI_PREP_CLOSURE_LOC
-       rc = ffi_prep_closure_loc(
-                       call->closure,
-                       &context->signature,
-                       psi_ffi_handler,
-                       impl,
-                       call->code);
-
-#elif PSI_HAVE_FFI_PREP_CLOSURE
-       rc = ffi_prep_closure(call->code, &context->signature, psi_ffi_handler, impl);
-#else
-# error "Neither ffi_prep_closure() nor ffi_prep_closure_loc() available"
-#endif
-       ZEND_ASSERT(FFI_OK == rc);
-}
-
-static inline void PSI_LibffiCallFree(PSI_LibffiCall *call) {
-       if (call->closure) {
-               psi_ffi_closure_free(call->closure);
-       }
-       free(call);
-}
 
 static inline PSI_LibffiContext *PSI_LibffiContextInit(PSI_LibffiContext *L) {
        ffi_status rc;
@@ -303,11 +362,6 @@ static inline PSI_LibffiContext *PSI_LibffiContextInit(PSI_LibffiContext *L) {
        return L;
 }
 
-static void psi_ffi_handler(ffi_cif *_sig, void *_result, void **_args, void *_data)
-{
-       psi_call(*(zend_execute_data **)_args[0], *(zval **)_args[1], _data);
-}
-
 static void psi_ffi_init(PSI_Context *C)
 {
        C->context = PSI_LibffiContextInit(NULL);
@@ -325,13 +379,33 @@ static void psi_ffi_dtor(PSI_Context *C)
                                PSI_LibffiCallFree(decl->call.info);
                        }
                }
+
+       }
+       if (C->impls) {
+               size_t i, j;
+
+               for (i = 0; i < C->impls->count; ++i) {
+                       impl *impl = C->impls->list[i];
+
+                       for (j = 0; j < impl->stmts->let.count; ++j) {
+                               let_stmt *let = impl->stmts->let.list[j];
+
+                               if (let->val && let->val->kind == PSI_LET_CALLBACK) {
+                                       let_callback *cb = let->val->data.callback;
+
+                                       if (cb->decl && cb->decl->call.info) {
+                                               PSI_LibffiCallFree(cb->decl->call.info);
+                                       }
+                               }
+                       }
+               }
        }
        free(C->context);
 }
 
 static zend_function_entry *psi_ffi_compile(PSI_Context *C)
 {
-       size_t i, j = 0;
+       size_t c, i, j = 0;
        zend_function_entry *zfe;
 
        if (!C->impls) {
@@ -349,19 +423,41 @@ static zend_function_entry *psi_ffi_compile(PSI_Context *C)
                }
 
                call = PSI_LibffiCallAlloc(C, impl->decl);
-               PSI_LibffiCallInitClosure(C, call, impl);
+               if (FFI_OK != PSI_LibffiCallInitClosure(C, call, impl)) {
+                       PSI_LibffiCallFree(call);
+                       continue;
+               }
 
                zf->fname = impl->func->name + (impl->func->name[0] == '\\');
                zf->num_args = impl->func->args->count;
                zf->handler = call->code;
                zf->arg_info = psi_internal_arginfo(impl);
                ++j;
+
+               for (c = 0; c < impl->stmts->let.count; ++c) {
+                       let_stmt *let = impl->stmts->let.list[c];
+
+                       if (let->val->kind == PSI_LET_CALLBACK) {
+                               let_callback *cb = let->val->data.callback;
+
+                               call = PSI_LibffiCallAlloc(C, cb->decl);
+                               if (FFI_OK != PSI_LibffiCallInitCallbackClosure(C, call, cb)) {
+                                       PSI_LibffiCallFree(call);
+                                       continue;
+                               }
+
+                               cb->decl->call.sym = call->code;
+                       }
+               }
        }
 
        for (i = 0; i < C->decls->count; ++i) {
                decl *decl = C->decls->list[i];
 
-               if (decl->impl) {
+//             if (decl->impl) {
+//                     continue;
+//             }
+               if (decl->call.info) {
                        continue;
                }
 
index c75a358bf8d9fbe9622d2d4f5a12645150546b51..f204753bfbf9190c13a0eb96f5bea4769a46f5b1 100644 (file)
@@ -4,7 +4,7 @@
 
 #include "php.h"
 #include "php_psi.h"
-
+#include "parser.h"
 #include "marshal.h"
 #include "calc.h"
 
@@ -13,12 +13,65 @@ void psi_to_void(zval *return_value, set_value *set, impl_val *ret_val)
        RETVAL_NULL();
 }
 
+void psi_to_zval(zval *return_value, set_value *set, impl_val *ret_val) {
+       RETVAL_ZVAL(ret_val->ptr, 1, 0);
+}
+
+impl_val *psi_let_zval(impl_val *tmp, decl_type *type, impl_arg *iarg, void **to_free)
+{
+       *to_free = tmp->ptr = emalloc(sizeof(zval));
+       ZVAL_COPY_VALUE(tmp->ptr, iarg->_zv);
+       return tmp;
+}
+
 void psi_to_bool(zval *return_value, set_value *set, impl_val *ret_val)
 {
        psi_to_int(return_value, set, ret_val);
        convert_to_boolean(return_value);
 }
 
+static inline impl_val *psi_val_boolval(impl_val *tmp, token_t real_type, zend_bool boolval) {
+       switch (real_type) {
+       case PSI_T_INT8:                tmp->i8 = boolval;              break;
+       case PSI_T_UINT8:               tmp->u8 = boolval;              break;
+       case PSI_T_INT16:               tmp->i16 = boolval;             break;
+       case PSI_T_UINT16:              tmp->u16 = boolval;             break;
+       case PSI_T_INT32:               tmp->i32 = boolval;             break;
+       case PSI_T_UINT32:              tmp->u32 = boolval;             break;
+       case PSI_T_INT64:               tmp->i64 = boolval;             break;
+       case PSI_T_UINT64:              tmp->u64 = boolval;             break;
+       case PSI_T_FLOAT:               tmp->fval = boolval;    break;
+       case PSI_T_DOUBLE:              tmp->dval = boolval;    break;
+#ifdef HAVE_LONG_DOUBLE
+       case PSI_T_LONG_DOUBLE: tmp->ldval = boolval;   break;
+#endif
+       EMPTY_SWITCH_DEFAULT_CASE();
+       }
+       return tmp;
+}
+
+impl_val *psi_let_boolval(impl_val *tmp, decl_type *type, impl_arg *iarg, void **to_free)
+{
+       zend_bool boolval;
+       token_t real_type = type ? real_decl_type(type)->type : PSI_T_UINT8;
+
+       if (iarg->type->type == PSI_T_BOOL) {
+               boolval = iarg->val.zend.bval;
+       } else {
+               boolval = zend_is_true(iarg->_zv);
+       }
+
+       return psi_val_boolval(tmp, real_type, boolval);
+}
+
+# define RETVAL_LONG_U64(V) \
+               if (V > ZEND_LONG_MAX) { \
+                       char d[24] = {0}; \
+                       RETVAL_STRING(zend_print_ulong_to_buf(&d[22], V)); \
+               } else { \
+                       RETVAL_LONG(V); \
+               }
+
 void psi_to_int(zval *return_value, set_value *set, impl_val *ret_val)
 {
        decl_var *var = set->vars->vars[0];
@@ -26,56 +79,59 @@ void psi_to_int(zval *return_value, set_value *set, impl_val *ret_val)
        impl_val *v = deref_impl_val(ret_val, var);
 
        switch (t) {
-       case PSI_T_FLOAT:
-               RETVAL_DOUBLE((double) v->fval);
-               convert_to_long(return_value);
-               break;
-       case PSI_T_DOUBLE:
-               RETVAL_DOUBLE(v->dval);
-               convert_to_long(return_value);
-               break;
-       case PSI_T_INT8:
-               RETVAL_LONG(v->i8);
-               break;
-       case PSI_T_UINT8:
-               RETVAL_LONG(v->u8);
-               break;
-       case PSI_T_INT16:
-               RETVAL_LONG(v->i16);
-               break;
-       case PSI_T_UINT16:
-               RETVAL_LONG(v->u16);
-               break;
-       case PSI_T_INT32:
-               RETVAL_LONG(v->i32);
-               break;
-       case PSI_T_UINT32:
-#if UINT32_MAX >= ZEND_LONG_MAX
-               if (v->u32 > ZEND_LONG_MAX) {
-                       char d[12] = {0};
-
-                       RETVAL_STRING(zend_print_ulong_to_buf(&d[10], v->u32));
-               } else {
-#endif
-                       RETVAL_LONG(v->u32);
-#if UINT32_MAX >= ZEND_LONG_MAX
-               }
+       case PSI_T_INT8:                RETVAL_LONG(v->i8);                                     break;
+       case PSI_T_UINT8:               RETVAL_LONG(v->u8);                                     break;
+       case PSI_T_INT16:               RETVAL_LONG(v->i16);                            break;
+       case PSI_T_UINT16:              RETVAL_LONG(v->u16);                            break;
+       case PSI_T_INT32:               RETVAL_LONG(v->i32);                            break;
+       case PSI_T_UINT32:              RETVAL_LONG(v->u32);                            break;
+       case PSI_T_INT64:               RETVAL_LONG(v->i64);                            break;
+       case PSI_T_UINT64:              RETVAL_LONG_U64(v->u64);                        break;
+       case PSI_T_FLOAT:               RETVAL_DOUBLE((double) v->fval);        break;
+       case PSI_T_DOUBLE:              RETVAL_DOUBLE(v->dval);                         break;
+#ifdef HAVE_LONG_DOUBLE
+       case PSI_T_LONG_DOUBLE: RETVAL_DOUBLE((double) v->ldval);       break;
 #endif
-               break;
-       case PSI_T_INT64:
-               RETVAL_LONG(v->i64);
-               break;
-       case PSI_T_UINT64:
-               if (v->u64 > ZEND_LONG_MAX) {
-                       char d[24] = {0};
+       EMPTY_SWITCH_DEFAULT_CASE();
+       }
 
-                       RETVAL_STRING(zend_print_ulong_to_buf(&d[22], v->u64));
-               } else {
-                       RETVAL_LONG(v->u64);
-               }
-               break;
+       convert_to_long(return_value);
+}
+
+static inline impl_val *psi_val_intval(impl_val *tmp, token_t real_type, zend_long intval) {
+       switch (real_type) {
+       case PSI_T_INT8:                tmp->i8 = intval;               break;
+       case PSI_T_UINT8:               tmp->u8 = intval;               break;
+       case PSI_T_INT16:               tmp->i16 = intval;              break;
+       case PSI_T_UINT16:              tmp->u16 = intval;              break;
+       case PSI_T_INT32:               tmp->i32 = intval;              break;
+       case PSI_T_UINT32:              tmp->u32 = intval;              break;
+       case PSI_T_INT:                 tmp->ival = intval;             break;
+       case PSI_T_INT64:               tmp->i64 = intval;              break;
+       case PSI_T_UINT64:              tmp->u64 = intval;              break;
+       case PSI_T_FLOAT:               tmp->fval = intval;             break;
+       case PSI_T_DOUBLE:              tmp->dval = intval;             break;
+#ifdef HAVE_LONG_DOUBLE
+       case PSI_T_LONG_DOUBLE: tmp->ldval = intval;    break;
+#endif
        EMPTY_SWITCH_DEFAULT_CASE();
        }
+
+       return tmp;
+}
+
+impl_val *psi_let_intval(impl_val *tmp, decl_type *type, impl_arg *iarg, void **to_free)
+{
+       zend_long intval;
+       token_t real_type = type ? real_decl_type(type)->type : PSI_T_INT;
+
+       if (iarg->type->type == PSI_T_INT) {
+               intval = iarg->val.zend.lval;
+       } else {
+               intval = zval_get_long(iarg->_zv);
+       }
+
+       return psi_val_intval(tmp, real_type, intval);
 }
 
 void psi_to_double(zval *return_value, set_value *set, impl_val *ret_val)
@@ -85,45 +141,58 @@ void psi_to_double(zval *return_value, set_value *set, impl_val *ret_val)
        impl_val *v = deref_impl_val(ret_val, var);
 
        switch (t) {
-       case PSI_T_FLOAT:
-               RETVAL_DOUBLE((double) v->fval);
-               break;
-       case PSI_T_DOUBLE:
-               RETVAL_DOUBLE(v->dval);
-               break;
+       case PSI_T_FLOAT:               RETVAL_DOUBLE((double) v->fval);        break;
+       case PSI_T_DOUBLE:              RETVAL_DOUBLE(v->dval);                         break;
 #ifdef HAVE_LONG_DOUBLE
-       case PSI_T_LONG_DOUBLE:
-               RETVAL_DOUBLE((double) v->ldval);
-               break;
+       case PSI_T_LONG_DOUBLE: RETVAL_DOUBLE((double) v->ldval);       break;
 #endif
-       case PSI_T_INT8:
-               RETVAL_DOUBLE((double) v->i8);
-               break;
-       case PSI_T_UINT8:
-               RETVAL_DOUBLE((double) v->u8);
-               break;
-       case PSI_T_INT16:
-               RETVAL_DOUBLE((double) v->i16);
-               break;
-       case PSI_T_UINT16:
-               RETVAL_DOUBLE((double) v->u16);
-               break;
-       case PSI_T_INT32:
-               RETVAL_DOUBLE((double) v->i32);
-               break;
-       case PSI_T_UINT32:
-               RETVAL_DOUBLE((double) v->u32);
-               break;
-       case PSI_T_INT64:
-               RETVAL_DOUBLE((double) v->i64);
-               break;
-       case PSI_T_UINT64:
-               RETVAL_DOUBLE((double) v->u64);
-               break;
+       case PSI_T_INT8:                RETVAL_DOUBLE((double) v->i8);          break;
+       case PSI_T_UINT8:               RETVAL_DOUBLE((double) v->u8);          break;
+       case PSI_T_INT16:               RETVAL_DOUBLE((double) v->i16);         break;
+       case PSI_T_UINT16:              RETVAL_DOUBLE((double) v->u16);         break;
+       case PSI_T_INT32:               RETVAL_DOUBLE((double) v->i32);         break;
+       case PSI_T_UINT32:              RETVAL_DOUBLE((double) v->u32);         break;
+       case PSI_T_INT64:               RETVAL_DOUBLE((double) v->i64);         break;
+       case PSI_T_UINT64:              RETVAL_DOUBLE((double) v->u64);         break;
        EMPTY_SWITCH_DEFAULT_CASE();
        }
 }
 
+static inline impl_val *psi_val_floatval(impl_val *tmp, token_t real_type, double floatval) {
+       switch (real_type) {
+       case PSI_T_INT8:                tmp->i8 = floatval;             break;
+       case PSI_T_UINT8:               tmp->u8 = floatval;             break;
+       case PSI_T_INT16:               tmp->i16 = floatval;    break;
+       case PSI_T_UINT16:              tmp->u16 = floatval;    break;
+       case PSI_T_INT32:               tmp->i32 = floatval;    break;
+       case PSI_T_UINT32:              tmp->u32 = floatval;    break;
+       case PSI_T_INT64:               tmp->i64 = floatval;    break;
+       case PSI_T_UINT64:              tmp->u64 = floatval;    break;
+       case PSI_T_FLOAT:               tmp->fval = floatval;   break;
+       case PSI_T_DOUBLE:              tmp->dval = floatval;   break;
+#ifdef HAVE_LONG_DOUBLE
+       case PSI_T_LONG_DOUBLE: tmp->ldval = floatval;  break;
+#endif
+       EMPTY_SWITCH_DEFAULT_CASE();
+       }
+
+       return tmp;
+}
+
+impl_val *psi_let_floatval(impl_val *tmp, decl_type *type, impl_arg *iarg, void **to_free)
+{
+       double floatval;
+       token_t real_type = type ? real_decl_type(type)->type : PSI_T_DOUBLE;
+
+       if (iarg->type->type == PSI_T_FLOAT || iarg->type->type == PSI_T_DOUBLE) {
+               floatval = iarg->val.dval;
+       } else {
+               floatval = zval_get_double(iarg->_zv);
+       }
+
+       return psi_val_floatval(tmp, real_type, floatval);
+}
+
 void psi_to_string(zval *return_value, set_value *set, impl_val *ret_val)
 {
        char *str;
@@ -131,16 +200,10 @@ void psi_to_string(zval *return_value, set_value *set, impl_val *ret_val)
        token_t t = real_decl_type(var->arg->type)->type;
 
        switch (t) {
-       case PSI_T_FLOAT:
-               RETVAL_DOUBLE((double) deref_impl_val(ret_val, var)->fval);
-               break;
-       case PSI_T_DOUBLE:
-               RETVAL_DOUBLE(deref_impl_val(ret_val, var)->dval);
-               break;
+       case PSI_T_FLOAT:               RETVAL_DOUBLE((double) deref_impl_val(ret_val, var)->fval);             break;
+       case PSI_T_DOUBLE:              RETVAL_DOUBLE(deref_impl_val(ret_val, var)->dval);                              break;
 #ifdef HAVE_LONG_DOUBLE
-       case PSI_T_LONG_DOUBLE:
-               RETVAL_DOUBLE((double) deref_impl_val(ret_val, var)->ldval);
-               break;
+       case PSI_T_LONG_DOUBLE: RETVAL_DOUBLE((double) deref_impl_val(ret_val, var)->ldval);    break;
 #endif
        default:
                if (!var->arg->var->pointer_level) {
@@ -165,9 +228,55 @@ void psi_to_string(zval *return_value, set_value *set, impl_val *ret_val)
                }
                return;
        }
+
        convert_to_string(return_value);
 }
 
+impl_val *psi_let_strval(impl_val *tmp, decl_type *type, impl_arg *iarg, void **to_free)
+{
+       if (iarg->type->type == PSI_T_STRING) {
+               if (iarg->val.zend.str) {
+                       tmp->ptr = estrndup(iarg->val.zend.str->val, iarg->val.zend.str->len);
+                       *to_free = tmp->ptr;
+               } else {
+                       tmp->ptr = "";
+               }
+       } else {
+               zend_string *zs = zval_get_string(iarg->_zv);
+               tmp->ptr = estrdup(zs->val);
+               *to_free = tmp->ptr;
+               zend_string_release(zs);
+       }
+
+       return tmp;
+}
+
+impl_val *psi_let_pathval(impl_val *tmp, decl_type *type, impl_arg *iarg, void **to_free)
+{
+       tmp = psi_let_strval(tmp, type, iarg, to_free);
+       if (SUCCESS != php_check_open_basedir(tmp->ptr)) {
+               efree(tmp->ptr);
+               return *to_free = NULL;
+       }
+       return tmp;
+}
+
+impl_val *psi_let_strlen(impl_val *tmp, decl_type *type, impl_arg *iarg, void **to_free)
+{
+       if (iarg->type->type == PSI_T_STRING) {
+               if (iarg->val.zend.str) {
+                       tmp->lval = iarg->val.zend.str->len;
+               } else {
+                       tmp->lval = 0;
+               }
+       } else {
+               zend_string *zs = zval_get_string(iarg->_zv);
+               tmp->lval = zs->len;
+               zend_string_release(zs);
+       }
+
+       return tmp;
+}
 
 static impl_val *iterate(impl_val *val, size_t size, unsigned i, impl_val *tmp)
 {
@@ -176,6 +285,59 @@ static impl_val *iterate(impl_val *val, size_t size, unsigned i, impl_val *tmp)
        return tmp;
 }
 
+void psi_from_zval_ex(impl_val **ptr, decl_arg *spec, token_t cast, zval *zv, void **tmp)
+{
+       decl_type *real = real_decl_type(spec->type);
+       impl_val *val = *ptr;
+
+       switch (real->type) {
+       default:
+               ZEND_ASSERT(0);
+               /* no break */
+       case PSI_T_INT8:
+               val->i8 = zval_get_long(zv);
+               break;
+       case PSI_T_UINT8:
+               val->u8 = zval_get_long(zv);
+               break;
+       case PSI_T_INT16:
+               val->i16 = zval_get_long(zv);
+               break;
+       case PSI_T_UINT16:
+               val->u16 = zval_get_long(zv);
+               break;
+       case PSI_T_INT32:
+               val->i32 = zval_get_long(zv);
+               break;
+       case PSI_T_UINT32:
+               val->u32 = zval_get_long(zv);
+               break;
+       case PSI_T_INT64:
+               val->i64 = zval_get_long(zv);
+               break;
+       case PSI_T_UINT64:
+               val->u64 = zval_get_long(zv);
+               break;
+       case PSI_T_FLOAT:
+               val->fval = zval_get_double(zv);
+               break;
+       case PSI_T_DOUBLE:
+               val->dval = zval_get_double(zv);
+               break;
+#ifdef HAVE_LONG_DOUBLE
+       case PSI_T_LONG_DOUBLE:
+               val->ldval = zval_get_double(zv);
+               break;
+#endif
+       case PSI_T_ENUM:
+               val->ival = zval_get_long(zv);
+               break;
+       case PSI_T_STRUCT:
+               *tmp = *ptr = psi_array_to_struct(real->strct, HASH_OF(zv));
+               break;
+       }
+}
+
 void psi_from_zval(impl_val *mem, decl_arg *spec, zval *zv, void **tmp)
 {
        decl_type *type = real_decl_type(spec->type);
@@ -248,10 +410,10 @@ void psi_to_array(zval *return_value, set_value *set, impl_val *r_val)
        if (t == PSI_T_STRUCT) {
                // decl_struct *s = real_decl_type(var->arg->type)->strct;
 
-               if (set->count) {
+               if (set->inner && set->inner->count) {
                        /* explicit member casts */
-                       for (i = 0; i < set->count; ++i) {
-                               set_value *sub_set = set->inner[i];
+                       for (i = 0; i < set->inner->count; ++i) {
+                               set_value *sub_set = set->inner->vals[i];
                                decl_var *sub_var = sub_set->vars->vars[0];
 
                                sub_set->outer.val = ret_val;
@@ -301,7 +463,7 @@ void psi_to_array(zval *return_value, set_value *set, impl_val *r_val)
                char *ptr;
                zend_long i, n = psi_long_num_exp(set->num, set->outer.val);
                size_t size = psi_t_size(var->arg->var->pointer_level ? PSI_T_POINTER : t);
-               set_value *sub_set = set->inner[0];
+               set_value *sub_set = set->inner->vals[0];
 
                sub_set->outer.val = set->outer.val;
                for (i = 0; i < n; ++i) {
@@ -314,7 +476,7 @@ void psi_to_array(zval *return_value, set_value *set, impl_val *r_val)
                zval ele;
                char *ptr = ret_val->ptr;
                size_t size = psi_t_size(var->arg->var->pointer_level ? PSI_T_POINTER : t);
-               set_value *sub_set = set->inner[0];
+               set_value *sub_set = set->inner->vals[0];
 
                sub_set->outer.val = set->outer.val;
                while (*(void **) ptr) {
@@ -325,6 +487,27 @@ void psi_to_array(zval *return_value, set_value *set, impl_val *r_val)
        }
 }
 
+impl_val *psi_let_arrval(impl_val *tmp, decl_type *type, impl_arg *iarg, void **to_free)
+{
+       decl_type *real = real_decl_type(type);
+       HashTable *arr;
+
+       if (iarg->type->type != PSI_T_ARRAY) {
+               SEPARATE_ARG_IF_REF(iarg->_zv);
+               convert_to_array(iarg->_zv);
+       }
+       arr = HASH_OF(iarg->_zv);
+
+       switch (real->type) {
+       case PSI_T_STRUCT:
+               *to_free = tmp = psi_array_to_struct(real->strct, arr);
+               break;
+       EMPTY_SWITCH_DEFAULT_CASE();
+       }
+
+       return tmp;
+}
+
 void psi_to_object(zval *return_value, set_value *set, impl_val *r_val)
 {
        decl_var *var = set->vars->vars[0];
@@ -339,3 +522,19 @@ void psi_to_object(zval *return_value, set_value *set, impl_val *r_val)
                RETVAL_NULL();
        }
 }
+
+impl_val *psi_let_objval(impl_val *tmp, decl_type *type, impl_arg *iarg, void **to_free)
+{
+       psi_object *obj;
+
+       if (Z_TYPE_P(iarg->_zv) != IS_OBJECT
+       ||      !instanceof_function(Z_OBJCE_P(iarg->_zv), psi_object_get_class_entry())) {
+               return NULL;
+       }
+
+       obj = PSI_OBJ(iarg->_zv, NULL);
+       tmp->ptr = obj->data;
+
+       return tmp;
+}
+
index 6b2d7fad14f663367e3e3dd65831f5f255fde146..47402e46cc06036b0b3377e464e1c7af19ac0525 100644 (file)
@@ -9,8 +9,19 @@ void psi_to_string(zval *return_value, set_value *set, impl_val *ret_val);
 void psi_to_recursive(zval *return_value, set_value *set, impl_val *r_val);
 void psi_to_array(zval *return_value, set_value *set, impl_val *ret_val);
 void psi_to_object(zval *return_value, set_value *set, impl_val *ret_val);
+void psi_to_zval(zval *return_value, set_value *set, impl_val *ret_val);
 
 void *psi_array_to_struct(decl_struct *s, HashTable *arr);
 void psi_from_zval(impl_val *mem, decl_arg *spec, zval *zv, void **tmp);
 
+impl_val *psi_let_boolval(impl_val *tmp, decl_type *type, impl_arg *iarg, void **to_free);
+impl_val *psi_let_intval(impl_val *tmp, decl_type *type, impl_arg *iarg, void **to_free);
+impl_val *psi_let_floatval(impl_val *tmp, decl_type *type, impl_arg *iarg, void **to_free);
+impl_val *psi_let_strval(impl_val *tmp, decl_type *type, impl_arg *iarg, void **to_free);
+impl_val *psi_let_pathval(impl_val *tmp, decl_type *type, impl_arg *iarg, void **to_free);
+impl_val *psi_let_strlen(impl_val *tmp, decl_type *type, impl_arg *iarg, void **to_free);
+impl_val *psi_let_arrval(impl_val *tmp, decl_type *type, impl_arg *iarg, void **to_free);
+impl_val *psi_let_objval(impl_val *tmp, decl_type *type, impl_arg *iarg, void **to_free);
+impl_val *psi_let_zval(impl_val *tmp, decl_type *type, impl_arg *iarg, void **to_free);
+
 #endif
index 94860c5248d0e0fd9e6076b4ae90e5e4ce5a2fe5..f16f8b853d717c3c49f463bd70f6e47580b59214 100644 (file)
@@ -7,6 +7,7 @@
 #include <string.h>
 
 #include <Zend/zend_types.h>
+#include <Zend/zend_API.h> /* fcall */
 
 #include "parser_proc.h"
 
@@ -29,6 +30,11 @@ typedef struct PSI_Token {
 
 static inline PSI_Token *PSI_TokenCopy(PSI_Token *src);
 
+typedef struct zend_fcall {
+       zend_fcall_info fci;
+       zend_fcall_info_cache fcc;
+} zend_fcall;
+
 typedef union impl_val {
        char cval;
        int8_t i8;
@@ -51,9 +57,10 @@ typedef union impl_val {
                zend_bool bval;
                zend_long lval;
                zend_string *str;
+               zend_fcall *cb;
        } zend;
+       zval zval;
        void *ptr;
-       uint8_t _dbg[sizeof(void *)];
 } impl_val;
 
 typedef struct decl_type {
@@ -150,7 +157,7 @@ typedef struct decl_arg {
        decl_type *type;
        decl_var *var;
        decl_struct_layout *layout;
-       struct let_stmt *let;
+       struct let_stmt *let; /* FIXME: decls must not point to impls !!! */
        impl_val val;
        void *ptr;
        void *mem;
@@ -470,6 +477,7 @@ static inline void free_impl_type(impl_type *type) {
 typedef struct impl_var {
        PSI_Token *token;
        char *name;
+       struct impl_arg *arg;
        unsigned reference:1;
 } impl_var;
 
@@ -480,6 +488,17 @@ static inline impl_var *init_impl_var(const char *name, int is_reference) {
        return var;
 }
 
+static inline impl_var *copy_impl_var(impl_var *var) {
+       impl_var *cpy = malloc(sizeof(*cpy));
+
+       memcpy(cpy, var, sizeof(*cpy));
+       cpy->name = strdup(cpy->name);
+       if (cpy->token) {
+               cpy->token = PSI_TokenCopy(cpy->token);
+       }
+       return cpy;
+}
+
 static inline void free_impl_var(impl_var *var) {
        if (var->token) {
                free(var->token);
@@ -579,6 +598,7 @@ static inline impl_arg *init_impl_arg(impl_type *type, impl_var *var, impl_def_v
        impl_arg *arg = calloc(1, sizeof(*arg));
        arg->type = type;
        arg->var = var;
+       arg->var->arg = arg;
        arg->def = def;
        return arg;
 }
@@ -877,11 +897,33 @@ static inline void free_let_calloc(let_calloc *alloc) {
        free(alloc);
 }
 
+typedef struct let_callback {
+       struct let_func *func;
+       struct set_values *args;
+       decl *decl;
+} let_callback;
+
+static inline void free_let_func(struct let_func *func);
+static inline void free_set_values(struct set_values *vals);
+static inline let_callback *init_let_callback(struct let_func *func, struct set_values *args) {
+       let_callback *cb = calloc(1, sizeof(*cb));
+
+       cb->func = func;
+       cb->args = args;
+       return cb;
+}
+
+static inline void free_let_callback(let_callback *cb) {
+       free_let_func(cb->func);
+       free_set_values(cb->args);
+       free(cb);
+}
+
 typedef struct let_func {
        token_t type;
        char *name;
        impl_var *var;
-       impl_arg *arg;
+       impl_val *(*handler)(impl_val *tmp, decl_type *type, impl_arg *iarg, void **to_free);
 } let_func;
 
 static inline let_func *init_let_func(token_t type, const char *name, impl_var *var) {
@@ -904,12 +946,14 @@ typedef struct let_val {
                PSI_LET_NULL,
                PSI_LET_NUMEXP,
                PSI_LET_CALLOC,
+               PSI_LET_CALLBACK,
                PSI_LET_FUNC,
                PSI_LET_TMP,
        } kind;
        union {
                num_exp *num;
                let_calloc *alloc;
+               let_callback *callback;
                let_func *func;
                decl_var *var;
        } data;
@@ -932,6 +976,9 @@ static inline let_val *init_let_val(enum let_val_kind kind, void *data) {
        case PSI_LET_CALLOC:
                let->data.alloc = data;
                break;
+       case PSI_LET_CALLBACK:
+               let->data.callback = data;
+               break;
        case PSI_LET_FUNC:
                let->data.func = data;
                break;
@@ -953,6 +1000,9 @@ static inline void free_let_val(let_val *let) {
        case PSI_LET_CALLOC:
                free_let_calloc(let->data.alloc);
                break;
+       case PSI_LET_CALLBACK:
+               free_let_callback(let->data.callback);
+               break;
        case PSI_LET_FUNC:
                free_let_func(let->data.func);
                break;
@@ -1021,19 +1071,25 @@ typedef struct set_value {
                struct set_value *set;
                impl_val *val;
        } outer;
-       struct set_value **inner;
-       size_t count;
+       struct set_values *inner;
 } set_value;
 
+typedef struct set_values {
+       set_value **vals;
+       size_t count;
+} set_values;
+
+
 static inline set_value *init_set_value(set_func *func, decl_vars *vars) {
        set_value *val = calloc(1, sizeof(*val));
        val->func = func;
        val->vars = vars;
        return val;
 }
+
+static inline set_values *add_set_value(set_values *vals, set_value *val);
 static inline set_value *add_inner_set_value(set_value *val, set_value *inner) {
-       val->inner = realloc(val->inner, ++val->count * sizeof(*val->inner));
-       val->inner[val->count-1] = inner;
+       val->inner = add_set_value(val->inner, inner);
        inner->outer.set = val;
        return val;
 }
@@ -1046,11 +1102,7 @@ static inline void free_set_value(set_value *val) {
                free_decl_vars(val->vars);
        }
        if (val->inner && (!val->outer.set || val->outer.set->inner != val->inner)) {
-               size_t i;
-               for (i = 0; i < val->count; ++i) {
-                       free_set_value(val->inner[i]);
-               }
-               free(val->inner);
+               free_set_values(val->inner);
        }
        if (val->num) {
                free_num_exp(val->num);
@@ -1058,6 +1110,37 @@ static inline void free_set_value(set_value *val) {
        free(val);
 }
 
+static inline set_values *init_set_values(set_value *val) {
+       set_values *vals = calloc(1, sizeof(*vals));
+       if (val) {
+               vals->count = 1;
+               vals->vals = calloc(1, sizeof(val));
+               vals->vals[0] = val;
+       }
+       return vals;
+}
+
+static inline set_values *add_set_value(set_values *vals, set_value *val) {
+       if (!vals) {
+               vals = calloc(1, sizeof(*vals));
+       }
+       vals->vals = realloc(vals->vals, ++vals->count * sizeof(val));
+       vals->vals[vals->count-1] = val;
+       return vals;
+}
+
+static inline void free_set_values(set_values *vals) {
+       if (vals->vals) {
+               size_t i;
+
+               for (i = 0; i < vals->count; ++i) {
+                       free_set_value(vals->vals[i]);
+               }
+               free(vals->vals);
+       }
+       free(vals);
+}
+
 typedef struct set_stmt {
        impl_var *var;
        set_value *val;
index 7e445fd8a0a99c1fc9296803fe920e792cdce7a0..61c328d815bab5f210b772c806f6fc60b9b02736 100644 (file)
@@ -214,6 +214,7 @@ token_t PSI_ParserScan(PSI_Parser *P)
                'STRING' {RETURN(PSI_T_STRING);}
                'ARRAY' {RETURN(PSI_T_ARRAY);}
                'OBJECT' {RETURN(PSI_T_OBJECT);}
+               'CALLBACK' {RETURN(PSI_T_CALLBACK);}
                'FUNCTION' {RETURN(PSI_T_FUNCTION);}
                'TYPEDEF' {RETURN(PSI_T_TYPEDEF);}
                'STRUCT' {RETURN(PSI_T_STRUCT);}
@@ -234,6 +235,7 @@ token_t PSI_ParserScan(PSI_Parser *P)
                'BOOLVAL' {RETURN(PSI_T_BOOLVAL);}
                'ARRVAL' {RETURN(PSI_T_ARRVAL);}
                'OBJVAL' {RETURN(PSI_T_OBJVAL);}
+               'ZVAL' {RETURN(PSI_T_ZVAL);}
                'CALLOC' {RETURN(PSI_T_CALLOC);}
                'TO_OBJECT' {RETURN(PSI_T_TO_OBJECT);}
                'TO_ARRAY' {RETURN(PSI_T_TO_ARRAY);}
index 8471c14bdb651379da6cbe4e11da526ab8d189a5..0ccde26bcd4b78f099a952ede50e4287b3bb51cf 100644 (file)
@@ -9,67 +9,69 @@
 #define PSI_T_LET                              9
 #define PSI_T_RETURN                          10
 #define PSI_T_CALLOC                          11
-#define PSI_T_LIB                             12
-#define PSI_T_STRING                          13
-#define PSI_T_EOF                             14
-#define PSI_T_EOS                             15
-#define PSI_T_QUOTED_STRING                   16
-#define PSI_T_ENUM                            17
-#define PSI_T_LBRACE                          18
-#define PSI_T_RBRACE                          19
-#define PSI_T_COMMA                           20
-#define PSI_T_EQUALS                          21
-#define PSI_T_UNION                           22
-#define PSI_T_STRUCT                          23
-#define PSI_T_COLON                           24
-#define PSI_T_LPAREN                          25
-#define PSI_T_NUMBER                          26
-#define PSI_T_RPAREN                          27
-#define PSI_T_BOOL                            28
-#define PSI_T_INT                             29
-#define PSI_T_FLOAT                           30
-#define PSI_T_CONST                           31
-#define PSI_T_NSNAME                          32
-#define PSI_T_TYPEDEF                         33
-#define PSI_T_VOID                            34
-#define PSI_T_LBRACKET                        35
-#define PSI_T_RBRACKET                        36
-#define PSI_T_ELLIPSIS                        37
-#define PSI_T_CHAR                            38
-#define PSI_T_SHORT                           39
-#define PSI_T_LONG                            40
-#define PSI_T_DOUBLE                          41
-#define PSI_T_UNSIGNED                        42
-#define PSI_T_SIGNED                          43
-#define PSI_T_INT8                            44
-#define PSI_T_UINT8                           45
-#define PSI_T_INT16                           46
-#define PSI_T_UINT16                          47
-#define PSI_T_INT32                           48
-#define PSI_T_UINT32                          49
-#define PSI_T_INT64                           50
-#define PSI_T_UINT64                          51
-#define PSI_T_FUNCTION                        52
-#define PSI_T_NULL                            53
-#define PSI_T_TRUE                            54
-#define PSI_T_FALSE                           55
-#define PSI_T_DOLLAR_NAME                     56
-#define PSI_T_OBJVAL                          57
-#define PSI_T_ARRVAL                          58
-#define PSI_T_PATHVAL                         59
-#define PSI_T_STRLEN                          60
-#define PSI_T_STRVAL                          61
-#define PSI_T_FLOATVAL                        62
-#define PSI_T_INTVAL                          63
-#define PSI_T_BOOLVAL                         64
-#define PSI_T_TO_OBJECT                       65
-#define PSI_T_TO_ARRAY                        66
-#define PSI_T_TO_STRING                       67
-#define PSI_T_TO_INT                          68
-#define PSI_T_TO_FLOAT                        69
-#define PSI_T_TO_BOOL                         70
-#define PSI_T_MIXED                           71
-#define PSI_T_ARRAY                           72
-#define PSI_T_OBJECT                          73
-#define PSI_T_CALLABLE                        74
-#define PSI_T_AMPERSAND                       75
+#define PSI_T_CALLBACK                        12
+#define PSI_T_ZVAL                            13
+#define PSI_T_LIB                             14
+#define PSI_T_STRING                          15
+#define PSI_T_EOF                             16
+#define PSI_T_EOS                             17
+#define PSI_T_QUOTED_STRING                   18
+#define PSI_T_ENUM                            19
+#define PSI_T_LBRACE                          20
+#define PSI_T_RBRACE                          21
+#define PSI_T_COMMA                           22
+#define PSI_T_EQUALS                          23
+#define PSI_T_UNION                           24
+#define PSI_T_STRUCT                          25
+#define PSI_T_COLON                           26
+#define PSI_T_LPAREN                          27
+#define PSI_T_NUMBER                          28
+#define PSI_T_RPAREN                          29
+#define PSI_T_BOOL                            30
+#define PSI_T_INT                             31
+#define PSI_T_FLOAT                           32
+#define PSI_T_CONST                           33
+#define PSI_T_NSNAME                          34
+#define PSI_T_TYPEDEF                         35
+#define PSI_T_VOID                            36
+#define PSI_T_LBRACKET                        37
+#define PSI_T_RBRACKET                        38
+#define PSI_T_ELLIPSIS                        39
+#define PSI_T_CHAR                            40
+#define PSI_T_SHORT                           41
+#define PSI_T_LONG                            42
+#define PSI_T_DOUBLE                          43
+#define PSI_T_UNSIGNED                        44
+#define PSI_T_SIGNED                          45
+#define PSI_T_INT8                            46
+#define PSI_T_UINT8                           47
+#define PSI_T_INT16                           48
+#define PSI_T_UINT16                          49
+#define PSI_T_INT32                           50
+#define PSI_T_UINT32                          51
+#define PSI_T_INT64                           52
+#define PSI_T_UINT64                          53
+#define PSI_T_FUNCTION                        54
+#define PSI_T_NULL                            55
+#define PSI_T_TRUE                            56
+#define PSI_T_FALSE                           57
+#define PSI_T_DOLLAR_NAME                     58
+#define PSI_T_OBJVAL                          59
+#define PSI_T_ARRVAL                          60
+#define PSI_T_PATHVAL                         61
+#define PSI_T_STRLEN                          62
+#define PSI_T_STRVAL                          63
+#define PSI_T_FLOATVAL                        64
+#define PSI_T_INTVAL                          65
+#define PSI_T_BOOLVAL                         66
+#define PSI_T_TO_OBJECT                       67
+#define PSI_T_TO_ARRAY                        68
+#define PSI_T_TO_STRING                       69
+#define PSI_T_TO_INT                          70
+#define PSI_T_TO_FLOAT                        71
+#define PSI_T_TO_BOOL                         72
+#define PSI_T_MIXED                           73
+#define PSI_T_ARRAY                           74
+#define PSI_T_OBJECT                          75
+#define PSI_T_CALLABLE                        76
+#define PSI_T_AMPERSAND                       77
index b4bf9600f9d2e2b675439d50dd0c1d9fa0dbe2f6..6a582cba4fffc4157d12383f9b97eb2a9ffbe2c2 100644 (file)
@@ -29,7 +29,7 @@ void psi_error(int, const char *, int, const char *, ...);
 %nonassoc NAME.
 %left PLUS MINUS.
 %left SLASH ASTERISK.
-%fallback NAME TEMP FREE SET LET RETURN CALLOC LIB STRING.
+%fallback NAME TEMP FREE SET LET RETURN CALLOC CALLBACK ZVAL LIB STRING.
 
 file ::= blocks.
 
@@ -700,6 +700,35 @@ let_stmt(let) ::= TEMP decl_var(var) EQUALS decl_var(val) EOS. {
        let = init_let_stmt(var, init_let_val(PSI_LET_TMP, val));
 }
 
+%type let_calloc {let_calloc*}
+%destructor let_calloc {free_let_calloc($$);}
+let_calloc(alloc) ::= num_exp(nmemb) COMMA num_exp(size). {
+       alloc = init_let_calloc(nmemb, size);
+}
+%token_class let_func_token ZVAL OBJVAL ARRVAL PATHVAL STRLEN STRVAL FLOATVAL INTVAL BOOLVAL.
+%type let_func {let_func*}
+%destructor let_func {free_let_func($$);}
+let_func(func) ::= let_func_token(T) LPAREN impl_var(var) RPAREN. {
+       func = init_let_func(T->type, T->text, var);
+       free(T);
+}
+
+%type callback_arg_list {set_values *}
+%destructor callback_arg_list {free_set_values($$);}
+callback_arg_list ::= .
+callback_arg_list(args) ::= callback_args(args_). {
+       args = args_;
+}
+
+%type callback_args {set_values *}
+%destructor callback_args {free_set_values($$);}
+callback_args(args) ::= set_value(val). {
+       args = init_set_values(val);
+}
+callback_args(args) ::= callback_args(args_) COMMA set_value(val). {
+       args = add_set_value(args_, val);
+}
+
 %type let_val {let_val*}
 %destructor let_val {free_let_val($$);}
 let_val(val) ::= NULL. {
@@ -714,20 +743,11 @@ let_val(val) ::= CALLOC LPAREN let_calloc(alloc) RPAREN. {
 let_val(val) ::= let_func(func). {
        val = init_let_val(PSI_LET_FUNC, func);
 }
-
-%type let_calloc {let_calloc*}
-%destructor let_calloc {free_let_calloc($$);}
-let_calloc(alloc) ::= num_exp(nmemb) COMMA num_exp(size). {
-       alloc = init_let_calloc(nmemb, size);
+let_val(val) ::= CALLBACK let_func_token(F) LPAREN impl_var(var) LPAREN callback_arg_list(args_) RPAREN RPAREN. {
+       val = init_let_val(PSI_LET_CALLBACK, init_let_callback(
+               init_let_func(F->type, F->text, var), args_));
+       free(F);
 }
-%token_class let_func_token OBJVAL ARRVAL PATHVAL STRLEN STRVAL FLOATVAL INTVAL BOOLVAL.
-%type let_func {let_func*}
-%destructor let_func {free_let_func($$);}
-let_func(func) ::= let_func_token(T) LPAREN impl_var(var) RPAREN. {
-       func = init_let_func(T->type, T->text, var);
-       free(T);
-}
-
 
 %type set_stmt {set_stmt*}
 %destructor set_stmt {free_set_stmt($$);}
@@ -769,7 +789,7 @@ set_vals(vals) ::= set_vals(vals_) COMMA set_value(val). {
        vals = add_inner_set_value(vals_, val);
 }
 
-%token_class set_func_token TO_OBJECT TO_ARRAY TO_STRING TO_INT TO_FLOAT TO_BOOL VOID.
+%token_class set_func_token TO_OBJECT TO_ARRAY TO_STRING TO_INT TO_FLOAT TO_BOOL ZVAL VOID.
 %type set_func {set_func*}
 %destructor set_func {free_set_func($$);}
 set_func(func) ::= set_func_token(T). {
diff --git a/tests/sqlite/sqlite.psi b/tests/sqlite/sqlite.psi
new file mode 100644 (file)
index 0000000..4fea4c6
--- /dev/null
@@ -0,0 +1,70 @@
+lib "sqlite3";
+
+extern const char *sqlite3_libversion(void);
+function sqlite3\version() : string {
+       return to_string(sqlite3_libversion);
+}
+
+extern const char *sqlite3_errstr(int errno);
+function sqlite3\errstr(int $errno) : string {
+       let errno = intval($errno);
+       return to_string(sqlite3_errstr);
+}
+
+/* obviously fake, we ever always need a pointer to it */
+struct sqlite3::(8, 8);
+
+typedef struct sqlite3 sqlite3;
+
+typedef int64_t sqlite_int64;
+typedef uint64_t sqlite_uint64;
+
+typedef sqlite_int64 sqlite3_int64;
+typedef sqlite_uint64 sqlite3_uint64;
+
+extern void sqlite3_free(void *ptr);
+function sqlite3\free(object $object) : void {
+       let ptr = objval($object);
+       return void(sqlite3_free);
+}
+
+extern int sqlite3_close(sqlite3 *db);
+function sqlite3\close(object $db) : int {
+       let db = objval($db);
+       return to_int(sqlite3_close);
+}
+
+extern int sqlite3_open(const char *filename, sqlite3 **db_ptr);
+function sqlite3\open(string $uri, object &$db) : int {
+       let filename = pathval($uri);
+       let db_ptr = &NULL;
+       return to_int(sqlite3_open);
+       set $db = to_object(*db_ptr);
+}
+
+typedef int (*sqlite3_callback)(void *data, int argc, char** argv, char** cols);
+
+/*
+ * C calls into us, so we have to have a way to define how the callback 
+ * arguments have to be marshaled for the userland callback, i.e. from 
+ * native C types to ZE zvals /and/ how the userland return value has
+ * to be marshaled to a native type. All in all, the opposite of function impls.
+ */
+
+extern int sqlite3_exec(sqlite3 *db, const char *sql, sqlite3_callback callback, void *data, char **errmsg);
+function sqlite3\exec(object $db, string $sql, callable $cb, mixed $cd, string &$error = null) : int {
+       let db = objval($db);
+       let sql = strval($sql);
+       let callback = callback intval(
+               $cb(
+                       zval(data), 
+                       to_int(argc), 
+                       to_array(argv, argc, to_string(argv)),
+                       to_array(cols, argc, to_string(cols))
+               )
+       );
+       let data = zval($cd);
+       let errmsg = &NULL;
+       return to_int(sqlite3_exec);
+       set $error = to_string(*errmsg);
+}
diff --git a/tests/sqlite/sqlite001.db b/tests/sqlite/sqlite001.db
new file mode 100644 (file)
index 0000000..0800e0e
Binary files /dev/null and b/tests/sqlite/sqlite001.db differ
diff --git a/tests/sqlite/sqlite001.phpt b/tests/sqlite/sqlite001.phpt
new file mode 100644 (file)
index 0000000..41b25e7
--- /dev/null
@@ -0,0 +1,86 @@
+--TEST--
+sqlite3
+--INI--
+psi.directory={PWD}:{PWD}/../../psi.d
+--SKIPIF--
+<?php
+extension_loaded("psi") or printf("%s\n", "skip - need ext/psi");
+function_exists("sqlite3\\open") or printf("%s\n", "skip - need libsqlite3");
+?>
+--FILE--
+===TEST===
+<?php
+
+var_dump(sqlite3\version());
+
+copy(__DIR__."/sqlite001.db", __DIR__."/sqlite001.tmp");
+
+$rc = sqlite3\open(__DIR__."/sqlite001.tmp", $db);
+if ($rc) {
+       printf("%s\n", sqlite3\errstr($rc));
+}
+
+function callback($context, int $argc, array $argv, array $cols) {
+       $context->row = $context->row ?? 0;
+
+       for ($i = 0; $i < $argc; ++$i) {
+               printf("%d: %s = %s\n", $context->row, $cols[$i], $argv[$i] ?? "<NULL>");
+       }
+       printf("\n");
+       ++$context->row;
+}
+
+$rc = sqlite3\exec($db, "SELECT * FROM test", "callback", new stdClass, $error);
+if ($rc) {
+       printf("%s: '%s'\n", sqlite3\errstr($rc), $error);
+}
+
+$rc = sqlite3\exec($db, "INSERT INTO test VALUES (3,'three')", "callback", new stdClass, $error);
+if ($rc) {
+       printf("%s: '%s'\n", sqlite3\errstr($rc), $error);
+}
+
+$rc = sqlite3\exec($db, "SELECT * FROM test", "callback", new stdClass, $error);
+if ($rc) {
+       printf("%s: '%s'\n", sqlite3\errstr($rc), $error);
+}
+
+$rc = sqlite3\exec($db, "SELECT *", "callback", new stdClass, $error);
+if ($rc) {
+       printf("%s: '%s'\n", sqlite3\errstr($rc), $error);
+}
+
+sqlite3\close($db);
+
+$rc = sqlite3\exec($db, "SELECT * FROM test", "callback", new stdClass, $error);
+if ($rc) {
+       printf("%s: '%s'\n", sqlite3\errstr($rc), $error);
+}
+
+?>
+===DONE===
+--EXPECTF--
+===TEST===
+string(%d) "3.%d.%s"
+0: id = 1
+0: data = one
+
+1: id = 2
+1: data = two
+
+0: id = 1
+0: data = one
+
+1: id = 2
+1: data = two
+
+2: id = 3
+2: data = three
+
+SQL logic error or missing database: 'no tables specified'
+library routine called out of sequence: ''
+===DONE===
+--CLEAN--
+<?php
+@unlink(__DIR__."/sqlite001.tmp");
+?>