flush
[m6w6/ext-psi] / src / context.c
1 #include <sys/param.h>
2 #include <dirent.h>
3 #include <fnmatch.h>
4 #include <errno.h>
5
6 #ifdef HAVE_CONFIG_H
7 # include "config.h"
8 #endif
9
10 #include "php.h"
11 #include "php_scandir.h"
12 #include "context.h"
13 #include "parser.h"
14 #include "validator.h"
15
16 #define psi_predef_count(s) (sizeof(psi_predef_ ##s## s)/sizeof(psi_predef ##s))
17 typedef struct psi_predef_type {
18 token_t type_tag;
19 const char *type_name;
20 const char *alias;
21 } psi_predef_type;
22 #define psi_predef_type_count() psi_predef_count(type)
23 static const psi_predef_types[] = {
24 PHP_PSI_TYPES
25 };
26
27 typedef struct psi_predef_const {
28 token_t type_tag;
29 const char *type_name;
30 const char *name;
31 const char *val_text;
32 token_t val_type_tag;
33 } psi_predef_const;
34 #define psi_predef_const_count() psi_predef_count(const)
35 static const psi_predef_consts[] = {
36 PHP_PSI_CONSTS
37 };
38
39 PSI_Context *PSI_ContextInit(PSI_Context *C, PSI_ContextOps *ops, PSI_ContextErrorFunc error)
40 {
41 size_t i;
42 PSI_Data data;
43
44 if (!C) {
45 C = malloc(sizeof(*C));
46 }
47 memset(C, 0, sizeof(*C));
48
49 C->error = error;
50 C->ops = ops;
51 ops->init(C);
52
53 memset(&data, 0, sizeof(data));
54 for (i = 0; i < psi_predef_type_count(); ++i) {
55 psi_predef_type *pre = &psi_predef_types[i];
56 decl_type *type = init_decl_type(pre->type_tag, pre->type_name);
57 decl_typedef *def = init_decl_typedef(pre->alias, type);
58
59 data.defs = add_decl_typedef(data.defs, def);
60 }
61 for (i = 0; i < psi_predef_const_count(); ++i) {
62 psi_predef_const *pre = psi_predef_const[i];
63 impl_def_val *val = init_impl_def_val(pre->val_type_tag, pre->val_text);
64 const_type *type = init_const_type(pre->type_tag, pre->type_name);
65 constant *constant = init_constant(type, pre->name, val);
66
67 data.consts = add_constant(data.consts, constant);
68 }
69 return C;
70 }
71
72 static int psi_select_dirent(const struct dirent *entry)
73 {
74 #ifndef FNM_CASEFOLD
75 #define FNM_CASEFOLD 0
76 #endif
77 return 0 == fnmatch("*.psi", entry->d_name, FNM_CASEFOLD);
78 }
79
80
81 void PSI_ContextBuild(PSI_Context *C, const char *path)
82 {
83 int i, n;
84 struct dirent **entries = NULL;
85
86 n = php_scandir(path, &entries, psi_select_dirent, alphasort);
87
88 if (n < 0) {
89 return;
90 } else for (i = 0; i < n; ++i) {
91 char psi[MAXPATHLEN];
92 PSI_Parser P;
93 PSI_Validator V;
94
95 if (MAXPATHLEN <= slprintf(psi, MAXPATHLEN, "%s/%s", path, entries[i]->d_name)) {
96 C->error(PSI_WARNING, "Path to PSI file too long: %s/%s",
97 path, entries[i]->d_name);
98 }
99 if (!PSI_ParserInit(&P, psi, C->error, 0)) {
100 C->error(PSI_WARNING, "Failed to init PSI parser (%s): %s",
101 psi, strerror(errno));
102 continue;
103 }
104
105 while (-1 != PSI_ParserScan(&P)) {
106 PSI_ParserParse(&P, PSI_TokenAlloc(&P));
107 };
108 PSI_ParserParse(&P, NULL);
109
110 if (!PSI_ValidatorInit(&V, &P)) {
111 C->error(PSI_WARNING, "Failed to init PSI validator");
112 break;
113 }
114 PSI_ParserDtor(&P);
115
116 if (PSI_ValidatorValidate(&V)) {
117 zend_function_entry *closures;
118
119 closures = PSI_ContextCompile(C, (PSI_Data *) &V);
120 if (closures && SUCCESS != zend_register_functions(NULL, closures, NULL, MODULE_PERSISTENT)) {
121 C->error(PSI_WARNING, "Failed to register functions!");
122 }
123 }
124 PSI_ValidatorDtor(&V);
125 }
126 if (entries) {
127 for (i = 0; i < n; ++i) {
128 free(entries[i]);
129 }
130 free(entries);
131 }
132
133 }
134
135 zend_function_entry *PSI_ContextCompile(PSI_Context *C, PSI_Data *D)
136 {
137 size_t i, count = C->count++;
138 zend_function_entry *zfe;
139
140 if (D->consts) {
141 zend_constant zc;
142
143 zc.flags = CONST_PERSISTENT|CONST_CS;
144 zc.module_number = EG(current_module)->module_number;
145
146 for (i = 0; i < D->consts->count; ++i) {
147 constant *c = D->consts->list[i];
148
149 zc.name = zend_string_init(c->name + (c->name[0] == '\\'), strlen(c->name) - (c->name[0] == '\\'), 1);
150 ZVAL_NEW_STR(&zc.value, zend_string_init(c->val->text, strlen(c->val->text), 1));
151
152 switch (c->type->type) {
153 case PSI_T_BOOL:
154 convert_to_boolean(&zc.value);
155 break;
156 case PSI_T_INT:
157 convert_to_long(&zc.value);
158 break;
159 case PSI_T_FLOAT:
160 convert_to_double(&zc.value);
161 break;
162 }
163 zend_register_constant(&zc);
164 }
165 }
166
167 C->data = realloc(C->data, C->count * sizeof(*C->data));
168 PSI_DataExchange(&C->data[count], D);
169
170 zfe = C->ops->compile(C, &C->data[count]);
171
172 C->closures = realloc(C->closures, C->count * sizeof(*C->closures));
173 C->closures[count] = zfe;
174
175 return zfe;
176 }
177
178 void PSI_ContextDtor(PSI_Context *C)
179 {
180 size_t i;
181
182 C->ops->dtor(C);
183
184 for (i = 0; i < C->count; ++i) {
185 PSI_DataDtor(&C->data[i]);
186 if (C->closures[i]){
187 free(C->closures[i]);
188 }
189 }
190 free(C->data);
191 free(C->closures);
192
193 memset(C, 0, sizeof(*C));
194 }
195
196 void PSI_ContextFree(PSI_Context **C)
197 {
198 if (*C) {
199 PSI_ContextDtor(*C);
200 free(*C);
201 *C = NULL;
202 }
203 }