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