support delimter separated pathlist
[m6w6/ext-psi] / src / context.c
index f0080f753e1ca6ec5aa15b2b5ec559e69e9a707b..e499c5f0d24052c0f5a32475f0f0878d2a0b1fc2 100644 (file)
@@ -49,6 +49,7 @@ typedef struct psi_predef_struct_member {
 #define PSI_PREDEF_STRUCT_MEMBERS 32
 typedef struct psi_predef_struct {
        const char *name;
+       size_t size;
        psi_predef_struct_member members[PSI_PREDEF_STRUCT_MEMBERS];
 } psi_predef_struct;
 static const psi_predef_struct psi_predef_structs[] = {
@@ -56,6 +57,17 @@ static const psi_predef_struct psi_predef_structs[] = {
 };
 #define psi_predef_struct_count() psi_predef_count(_struct)
 
+PHP_PSI_MACROS
+
+typedef struct psi_predef_func {
+       const char *name;
+       void (*func)(void);
+} psi_predef_func;
+static psi_predef_func psi_predef_funcs[] = {
+       PHP_PSI_FUNCS{0}
+};
+#define psi_predef_func_count() psi_predef_count(_func)
+
 static int validate_lib(PSI_Data *data, void **dlopened) {
        char lib[MAXPATHLEN];
        const char *ptr = data->psi.file.ln;
@@ -65,11 +77,7 @@ static int validate_lib(PSI_Data *data, void **dlopened) {
                /* FIXME: assume stdlib */
                return 1;
        } else if (!strchr(ptr, '/')) {
-#ifdef DARWIN
-               len = snprintf(lib, MAXPATHLEN, "lib%s.dylib", ptr);
-#else
-               len = snprintf(lib, MAXPATHLEN, "lib%s.so", ptr);
-#endif
+               len = snprintf(lib, MAXPATHLEN, "lib%s.%s", ptr, PHP_PSI_SHLIB_SUFFIX);
                if (MAXPATHLEN == len) {
                        data->error(PSI_WARNING, "Library name too long: '%s'", ptr);
                }
@@ -78,7 +86,7 @@ static int validate_lib(PSI_Data *data, void **dlopened) {
        }
        if (!(*dlopened = dlopen(ptr, RTLD_LAZY|RTLD_LOCAL))) {
                data->error(PSI_WARNING, "Could not open library '%s': %s.",
-                               data->psi.file.fn, dlerror());
+                               data->psi.file.ln, dlerror());
                return 0;
        }
        return 1;
@@ -168,7 +176,7 @@ static inline int validate_decl_struct(PSI_Data *data, decl_struct *s) {
                        return 0;
                }
 
-               ZEND_ASSERT(!darg->var->arg);
+               ZEND_ASSERT(!darg->var->arg || darg->var->arg == darg);
                darg->var->arg = darg;
 
                if (!darg->layout) {
@@ -229,8 +237,20 @@ static inline int validate_decl_func(PSI_Data *data, void *dl, decl *decl, decl_
 #endif
        decl->dlptr = dlsym(dl ?: RTLD_NEXT, func->var->name);
        if (!decl->dlptr) {
-               data->error(PSI_WARNING, "Failed to located symbol '%s': %s",
-                       func->var->name, dlerror());
+               size_t i;
+
+               for (i = 0; i < psi_predef_func_count(); ++i) {
+                       psi_predef_func *pre = &psi_predef_funcs[i];
+
+                       if (!strcmp(func->var->name, pre->name)) {
+                               decl->dlptr = pre->func;
+                               break;
+                       }
+               }
+               if (!decl->dlptr) {
+                       data->error(PSI_WARNING, "Failed to locate symbol '%s': %s",
+                               func->var->name, dlerror());
+               }
        }
        return 1;
 }
@@ -254,18 +274,79 @@ static inline int validate_decl(PSI_Data *data, void *dl, decl *decl) {
        return 1;
 }
 
+static inline decl_arg *locate_struct_member(decl_struct *s, decl_var *var) {
+       size_t i;
+
+       ZEND_ASSERT(s);
+       for (i = 0; i < s->args->count; ++i) {
+               decl_arg *darg = s->args->args[i];
+
+               if (!strcmp(var->name, darg->var->name)) {
+                       return var->arg = darg;
+               }
+       }
+
+       return NULL;
+}
+static inline int validate_set_value(PSI_Data *data, set_value *set, decl_arg *ref) {
+       size_t i;
+       decl_type *ref_type = real_decl_type(ref->type);
+       decl_var *set_var = set->vars->vars[0];
+
+       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;
+       EMPTY_SWITCH_DEFAULT_CASE();
+       }
+
+       if (strcmp(set_var->name, ref->var->name)) {
+               return 0;
+       }
+
+       if (set->count && (set->func->type != PSI_T_TO_ARRAY || ref_type->type != PSI_T_STRUCT)) {
+               data->error(E_WARNING, "Inner `set` statement casts only work with to_array() casts on structs");
+               return 0;
+       }
+       for (i = 0; i < set->count; ++i) {
+               decl_var *sub_var = set->inner[i]->vars->vars[0];
+               decl_arg *sub_ref = locate_struct_member(ref_type->strct, sub_var);
+
+               if (sub_ref) {
+                       if (!validate_set_value(data, set->inner[i], sub_ref)) {
+                               return 0;
+                       }
+               }
+       }
+
+       return 1;
+}
 static inline decl *locate_impl_decl(decls *decls, return_stmt *ret) {
        size_t i;
 
        for (i = 0; i < decls->count; ++i) {
-               if (!strcmp(decls->list[i]->func->var->name, ret->decl->name)) {
-                       ret->decl->arg = decls->list[i]->func;
+               if (!strcmp(decls->list[i]->func->var->name, ret->set->vars->vars[0]->name)) {
+                       ret->decl = decls->list[i]->func;
                        return decls->list[i];
                }
        }
        return NULL;
 }
 static inline int validate_impl_ret_stmt(PSI_Data *data, impl *impl) {
+       return_stmt *ret;
+
        /* we must have exactly one ret stmt delcaring the native func to call */
        /* and which type cast to apply */
        if (impl->stmts->ret.count != 1) {
@@ -279,11 +360,17 @@ static inline int validate_impl_ret_stmt(PSI_Data *data, impl *impl) {
                }
                return 0;
        }
-       if (!(impl->decl = locate_impl_decl(data->decls, impl->stmts->ret.list[0]))) {
+
+       ret = impl->stmts->ret.list[0];
+
+       if (!(impl->decl = locate_impl_decl(data->decls, ret))) {
                data->error(PSI_WARNING, "Missing declaration for implementation %s",
                                impl->func->name);
                return 0;
        }
+       if (!validate_set_value(data, ret->set, ret->decl)) {
+               return 0;
+       }
 
        return 1;
 }
@@ -377,6 +464,9 @@ static inline int validate_impl_set_stmts(PSI_Data *data, impl *impl) {
 
                                if (!strcmp(set_var->name, set_arg->var->name)) {
                                        check = 1;
+                                       if (!validate_set_value(data, set->val, set_arg)) {
+                                               return 0;
+                                       }
                                        set_var->arg = set_arg;
                                        break;
                                }
@@ -392,34 +482,59 @@ static inline int validate_impl_set_stmts(PSI_Data *data, impl *impl) {
        }
        return 1;
 }
+static inline decl *locate_free_decl(decls *decls, free_call *f) {
+       size_t i;
+
+       for (i = 0; i < decls->count; ++i) {
+               if (!strcmp(decls->list[i]->func->var->name, f->func)) {
+                       f->decl = decls->list[i];
+                       return decls->list[i];
+               }
+       }
+       return NULL;
+}
 static inline int validate_impl_free_stmts(PSI_Data *data, impl *impl) {
-       size_t i, j, k;
+       size_t i, j, k, l;
        /* we can have any count of free stmts; freeing any out vars */
        for (i = 0; i < impl->stmts->fre.count; ++i) {
                free_stmt *fre = impl->stmts->fre.list[i];
 
-               for (j = 0; j < fre->vars->count; ++j) {
-                       decl_var *free_var = fre->vars->vars[j];
-                       int check = 0;
+               for (j = 0; j < fre->calls->count; ++j) {
+                       free_call *free_call = fre->calls->list[j];
 
-                       if (!strcmp(free_var->name, impl->decl->func->var->name)) {
-                               continue;
+                       /* first find the decl of the free func */
+                       if (!locate_free_decl(data->decls, free_call)) {
+                               data->error(PSI_WARNING, "Unknown function '%s' in `free` statement"
+                                               " of implementation '%s'", free_call->func, impl->func->name);
+                               return 0;
+                       }
+                       if (!impl->decl->args) {
+                               data->error(PSI_WARNING, "Declaration '%s' of implementation '%s'"
+                                               " does not have any arguments to free",
+                                               impl->decl->func->var->name, impl->func->name);
                        }
-                       if (impl->decl->args) for (k = 0; k < impl->decl->args->count; ++k) {
-                               decl_arg *free_arg = impl->decl->args->args[k];
 
-                               if (!strcmp(free_var->name, free_arg->var->name)) {
-                                       check = 1;
-                                       free_var->arg = free_arg;
-                                       break;
+                       /* now check for known vars */
+                       for (l = 0; l < free_call->vars->count; ++l) {
+                               int check = 0;
+                               decl_var *free_var = free_call->vars->vars[l];
+
+                               for (k = 0; k < impl->decl->args->count; ++k) {
+                                       decl_arg *free_arg = impl->decl->args->args[k];
+
+                                       if (!strcmp(free_var->name, free_arg->var->name)) {
+                                               check = 1;
+                                               free_var->arg = free_arg;
+                                               break;
+                                       }
                                }
-                       }
 
-                       if (!check) {
-                               data->error(PSI_WARNING, "Unknown variable '%s' of `free` statement"
-                                               " of implementation '%s'",
-                                               free_var->name, impl->func->name);
-                               return 0;
+                               if (!check) {
+                                       data->error(PSI_WARNING, "Unknown variable '%s' of `free` statement"
+                                                       " of implementation '%s'",
+                                                       free_var->name, impl->func->name);
+                                       return 0;
+                               }
                        }
                }
        }
@@ -505,6 +620,7 @@ PSI_Context *PSI_ContextInit(PSI_Context *C, PSI_ContextOps *ops, PSI_ContextErr
                }
 
                dstruct = init_decl_struct(pre->name, dargs);
+               dstruct->size = pre->size;
                T.structs = add_decl_struct(T.structs, dstruct);
        }
 
@@ -612,47 +728,62 @@ static int psi_select_dirent(const struct dirent *entry)
        return 0 == fnmatch("*.psi", entry->d_name, FNM_CASEFOLD);
 }
 
-void PSI_ContextBuild(PSI_Context *C, const char *path)
+void PSI_ContextBuild(PSI_Context *C, const char *paths)
 {
        int i, n;
+       char *sep = NULL, *cpy = strdup(paths), *ptr = cpy;
        struct dirent **entries = NULL;
 
-       n = php_scandir(path, &entries, psi_select_dirent, alphasort);
 
-       if (n < 0) {
-               return;
-       } else for (i = 0; i < n; ++i) {
-               char psi[MAXPATHLEN];
-               PSI_Parser P;
+       do {
+               sep = strchr(ptr, ':');
 
-               if (MAXPATHLEN <= slprintf(psi, MAXPATHLEN, "%s/%s", path, entries[i]->d_name)) {
-                       C->error(PSI_WARNING, "Path to PSI file too long: %s/%s",
-                               path, entries[i]->d_name);
+               if (sep) {
+                       *sep = 0;
                }
-               if (!PSI_ParserInit(&P, psi, C->error, 0)) {
-                       C->error(PSI_WARNING, "Failed to init PSI parser (%s): %s",
-                               psi, strerror(errno));
-                       continue;
+
+               n = php_scandir(ptr, &entries, psi_select_dirent, alphasort);
+
+               if (n > 0) {
+                       for (i = 0; i < n; ++i) {
+                               char psi[MAXPATHLEN];
+                               PSI_Parser P;
+
+                               if (MAXPATHLEN <= slprintf(psi, MAXPATHLEN, "%s/%s", ptr, entries[i]->d_name)) {
+                                       C->error(PSI_WARNING, "Path to PSI file too long: %s/%s",
+                                               ptr, entries[i]->d_name);
+                               }
+                               if (!PSI_ParserInit(&P, psi, C->error, 0)) {
+                                       C->error(PSI_WARNING, "Failed to init PSI parser (%s): %s",
+                                               psi, strerror(errno));
+                                       continue;
+                               }
+
+                               while (-1 != PSI_ParserScan(&P)) {
+                                       PSI_ParserParse(&P, PSI_TokenAlloc(&P));
+                               };
+                               PSI_ParserParse(&P, NULL);
+                               PSI_ContextValidate(C, &P);
+                               PSI_ParserDtor(&P);
+                       }
                }
 
-               while (-1 != PSI_ParserScan(&P)) {
-                       PSI_ParserParse(&P, PSI_TokenAlloc(&P));
-               };
-               PSI_ParserParse(&P, NULL);
-               PSI_ContextValidate(C, &P);
-               PSI_ParserDtor(&P);
-       }
+               if (entries) {
+                       for (i = 0; i < n; ++i) {
+                               free(entries[i]);
+                       }
+                       free(entries);
+               }
+
+               ptr = sep + 1;
+       } while (sep);
+
 
        if (PSI_ContextCompile(C) && SUCCESS != zend_register_functions(NULL, C->closures, NULL, MODULE_PERSISTENT)) {
                C->error(PSI_WARNING, "Failed to register functions!");
        }
 
-       if (entries) {
-               for (i = 0; i < n; ++i) {
-                       free(entries[i]);
-               }
-               free(entries);
-       }
+       free(cpy);
 
 }
 
@@ -691,6 +822,12 @@ zend_function_entry *PSI_ContextCompile(PSI_Context *C)
        return C->closures = C->ops->compile(C);
 }
 
+
+void PSI_ContextCall(PSI_Context *C, impl_val *ret_val, decl *decl, impl_val **args)
+{
+       C->ops->call(C, ret_val, decl, args);
+}
+
 void PSI_ContextDtor(PSI_Context *C)
 {
        size_t i;