--- /dev/null
+/tests/sqlite/sqlite001.db binary
* pemalloc
* unions
* callbacks and function pointers
+* let impl_Var point to impl_arg, just like decl_var?
\ No newline at end of file
}
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);
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);
#include "php_psi_macros.h"
#include "php_psi_redirs.h"
+#include "parser.h"
#include "calc.h"
#include "marshal.h"
#include "engine.h"
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);
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;
}
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;
}
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) {
}
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;
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 */
}
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);
}
}
- 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) {
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;
}
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;
}
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;
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;
#include "zend_exceptions.h"
+#include "parser.h"
#include "engine.h"
#include "calc.h"
#include "marshal.h"
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;
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:
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) {
}
}
-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);
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;
}
}
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);
}
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)
#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:
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));
}
}
-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;
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);
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) {
}
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;
}
#include "php.h"
#include "php_psi.h"
-
+#include "parser.h"
#include "marshal.h"
#include "calc.h"
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];
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)
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;
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) {
}
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)
{
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);
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;
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) {
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) {
}
}
+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];
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;
+}
+
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
#include <string.h>
#include <Zend/zend_types.h>
+#include <Zend/zend_API.h> /* fcall */
#include "parser_proc.h"
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;
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 {
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;
typedef struct impl_var {
PSI_Token *token;
char *name;
+ struct impl_arg *arg;
unsigned reference:1;
} impl_var;
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);
impl_arg *arg = calloc(1, sizeof(*arg));
arg->type = type;
arg->var = var;
+ arg->var->arg = arg;
arg->def = def;
return arg;
}
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) {
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;
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;
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;
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;
}
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);
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;
'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);}
'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);}
#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
%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.
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. {
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($$);}
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). {
--- /dev/null
+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);
+}
--- /dev/null
+--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");
+?>