+++ /dev/null
-/*******************************************************************************
- Copyright (c) 2016, Michael Wallner <mike@php.net>.
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*******************************************************************************/
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#else
-# include "php_config.h"
-#endif
-
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "data.h"
-#include "marshal.h"
-
-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;
-}
-
-set_value *add_inner_set_value(set_value *val, set_value *inner) {
- val->inner = add_set_value(val->inner, inner);
- inner->outer.set = val;
- return val;
-}
-
-void free_set_value(set_value *val) {
- if (val->func) {
- free_set_func(val->func);
- }
- if (val->vars) {
- free_decl_vars(val->vars);
- }
- if (val->inner
- && (!val->outer.set || val->outer.set->inner != val->inner)) {
- free_set_values(val->inner);
- }
- if (val->num) {
- free_num_exp(val->num);
- }
- free(val);
-}
-
-void dump_set_value(int fd, set_value *set, unsigned level, int last) {
- size_t i;
-
- if (level > 1) {
- /* only if not directly after `set ...` */
- dprintf(fd, "%s", psi_t_indent(level));
- }
-
- if (set->func->type == PSI_T_ELLIPSIS) {
- dprintf(fd, "%s(", set->outer.set->func->name);
- } else {
- dprintf(fd, "%s(", set->func->name);
- }
-
- for (i = 0; i < set->vars->count; ++i) {
- decl_var *svar = set->vars->vars[i];
- if (i) {
- dprintf(fd, ", ");
- }
- dump_decl_var(fd, svar);
- }
-
- if (set->func->type == PSI_T_ELLIPSIS) {
- dprintf(fd, ", ...");
- }
- if (set->num) {
- dprintf(fd, ", ");
- dump_num_exp(fd, set->num);
- }
- if (set->inner && set->inner->vals && set->func->type != PSI_T_ELLIPSIS) {
- dprintf(fd, ",\n");
- for (i = 0; i < set->inner->count; ++i) {
- dump_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 */
- dprintf(fd, "%s", psi_t_indent(level));
- }
- if (level > 1) {
- dprintf(fd, ")%s\n", last ? "" : ",");
- } else {
- dprintf(fd, ");");
- }
-}
-
-static inline void decl_var_arg_v(decl_args *args, va_list argp) {
- int argc;
- decl_arg **argv;
-
- memset(args, 0, sizeof(*args));
-
- while ((argc = va_arg(argp, int))) {
- argv = va_arg(argp, decl_arg **);
- while (argc--) {
- add_decl_arg(args, *argv++);
- }
- }
-}
-
-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_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;
- break;
- }
- /* no break */
- default:
- return 0;
- }
- return 1;
-}
-
-int validate_set_value(struct psi_data *data, set_value *set, ...) {
- va_list argp;
- decl_args args = {0};
- int check;
-
- va_start(argp, set);
- decl_var_arg_v(&args, argp);
- va_end(argp);
-
- check = validate_set_value_ex(data, set, NULL, &args);
- if (args.args) {
- free(args.args);
- }
- return check;
-}
-
-int validate_set_value_ex(struct psi_data *data, set_value *set, decl_arg *ref, decl_args *ref_list) {
- size_t i;
- decl_type *ref_type;
- decl_var *set_var = set->vars->vars[0];
-
- if (!validate_set_value_handler(set)) {
- data->error(data, set->func->token, PSI_WARNING, "Invalid cast '%s' in `set` statement", set->func->name);
- return 0;
- }
-
- for (i = 0; i < set->vars->count; ++i) {
- decl_var *svar = set->vars->vars[i];
- if (!svar->arg && !locate_decl_var_arg(svar, ref_list, NULL)) {
- data->error(data, svar->token, PSI_WARNING, "Unknown variable '%s' in `set` statement", svar->name);
- return 0;
- }
- }
-
- if (!ref) {
- ref = set_var->arg;
- }
- ref_type = real_decl_type(ref->type);
-
- 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 (!is_to_array && !is_pointer_to_struct) {
- data->error(data, set->func->token, E_WARNING, "Inner `set` statement casts only work with "
- "to_array() casts on structs or pointers: %s(%s...", set->func->name, set->vars->vars[0]->name);
- return 0;
- }
- }
- if (set->num) {
- if (!validate_num_exp(data, set->num, ref_list, ref, NULL)) {
- return 0;
- }
- }
-
- if (set->inner && (ref_type->type == PSI_T_STRUCT || ref_type->type == PSI_T_UNION)) {
- /* to_array(struct, to_...) */
- 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;
-
- switch (ref_type->type) {
- case PSI_T_STRUCT:
- sub_ref = locate_decl_struct_member(ref_type->real.strct, sub_var);
- break;
- case PSI_T_UNION:
- sub_ref = locate_decl_union_member(ref_type->real.unn, sub_var);
- break;
- }
-
- if (sub_ref) {
- if (!validate_set_value_ex(data, set->inner->vals[i], sub_ref, ref_type->real.strct->args)) {
- return 0;
- }
- }
- }
- }
- } else if (set->inner && set->inner->count == 1) {
- /* to_array(ptr, to_string(*ptr)) */
- 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) {
- if (strcmp(sub_var->name, set_var->name)) {
- 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->vals[0], sub_ref, ref_list)) {
- return 0;
- }
- }
- } 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, "
- "unless the outer type is a struct or union, got '%s%s'",
- ref_type->name, psi_t_indirection(ref->var->pointer_level));
- return 0;
- }
-
- return 1;
-}
-