5d9d7e42f00dd9574fd9548bd8b65a696cfaa889
[m6w6/ext-psi] / src / builtin.c
1 /*******************************************************************************
2 Copyright (c) 2018, Michael Wallner <mike@php.net>.
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8 * Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
18 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *******************************************************************************/
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #else
29 # include "php_config.h"
30 #endif
31
32 #include <stdbool.h>
33 #include <stdarg.h>
34
35 #include "php_psi.h"
36 #include "builtin.h"
37 #include "parser.h"
38 #include "cpp.h"
39
40 HashTable psi_builtins;
41
42 static bool has_include(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
43 static bool has_include_next(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
44 static bool has_feature(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
45 static bool builtin_constant_p(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
46 static bool BASE_FILE__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
47 static bool COUNTER__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
48 static bool FILE__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
49 static bool INCLUDE_LEVEL__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
50 static bool LINE__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
51 static bool TIMESTAMP__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
52
53 static inline struct psi_plist *builtin_sig(token_t typ, ...)
54 {
55 struct psi_plist *sig;
56 size_t n = 0;
57 va_list args;
58
59 if (typ == (token_t) - 1) {
60 return NULL;
61 }
62
63 sig = psi_plist_init((psi_plist_dtor) psi_token_free);
64 va_start(args, typ);
65 while (typ) {
66 char a = 'a' + n++;
67 struct psi_token *arg;
68
69 arg = psi_token_init(typ, &a, 1, 0, 0, zend_empty_string);
70 sig = psi_plist_add(sig, &arg);
71 typ = va_arg(args, token_t);
72 }
73 va_end(args);
74
75 return sig;
76 }
77
78 static void free_builtin(zval *p)
79 {
80 struct psi_builtin *b = Z_PTR_P(p);
81
82 if (b) {
83 zend_string_release(b->name);
84 psi_cpp_macro_decl_free(&b->decl);
85 pefree(b, 1);
86 }
87 }
88
89 PHP_MINIT_FUNCTION(psi_builtin);
90 PHP_MINIT_FUNCTION(psi_builtin)
91 {
92 #define PSI_BUILTIN(builtin, ...) do { \
93 struct psi_builtin entry; \
94 struct psi_plist *sig = builtin_sig(__VA_ARGS__, 0); \
95 struct psi_cpp_macro_decl *decl = psi_cpp_macro_decl_init(sig, NULL, NULL); \
96 decl->token = psi_token_init(PSI_T_NAME, "__" #builtin, sizeof("__" #builtin)-1, \
97 0, 0, zend_empty_string); \
98 entry.name = zend_string_copy(decl->token->text); \
99 entry.func = &builtin; \
100 entry.decl = decl; \
101 zend_hash_add_mem(&psi_builtins, entry.name, &entry, sizeof(entry)); \
102 } while(0)
103
104 zend_hash_init(&psi_builtins, 0, NULL, free_builtin, 1);
105 PSI_BUILTIN(has_include, PSI_T_CPP_HEADER);
106 PSI_BUILTIN(has_include_next, PSI_T_CPP_HEADER);
107 PSI_BUILTIN(has_feature, PSI_T_NAME);
108 PSI_BUILTIN(builtin_constant_p, PSI_T_NAME);
109
110 PSI_BUILTIN(BASE_FILE__, -1);
111 PSI_BUILTIN(COUNTER__, -1);
112 PSI_BUILTIN(FILE__, -1);
113 PSI_BUILTIN(INCLUDE_LEVEL__, -1);
114 PSI_BUILTIN(LINE__, -1);
115 PSI_BUILTIN(TIMESTAMP__, -1);
116
117 return SUCCESS;
118 }
119
120 PHP_MSHUTDOWN_FUNCTION(psi_builtin);
121 PHP_MSHUTDOWN_FUNCTION(psi_builtin)
122 {
123 zend_hash_destroy(&psi_builtins);
124 return SUCCESS;
125 }
126
127 bool psi_builtin_exists(zend_string *name)
128 {
129 return zend_hash_exists(&psi_builtins, name);
130 }
131
132 struct psi_builtin *psi_builtin_get(zend_string *name)
133 {
134 return zend_hash_find_ptr(&psi_builtins, name);
135 }
136
137 static bool has_include(struct psi_cpp *cpp, struct psi_token *target,
138 struct psi_plist **args, struct psi_plist **res)
139 {
140 struct psi_plist *arg = args[0];
141 struct psi_token *tok;
142
143 switch (psi_plist_count(arg)) {
144 case 1:
145 if (psi_plist_get(arg, 0, &tok)) {
146 const char *cpp_search = cpp->search;
147 bool has = psi_cpp_has_include(cpp, tok, 0, NULL);
148 cpp->search = cpp_search;
149 return has;
150 }
151 /* no break */
152 default:
153 cpp->parser->error(PSI_DATA(cpp->parser), target, PSI_WARNING,
154 "Erroneous usage of builtin __%s", __FUNCTION__);
155 }
156 return false;
157 }
158
159 static bool has_include_next(struct psi_cpp *cpp, struct psi_token *target,
160 struct psi_plist **args, struct psi_plist **res)
161 {
162 struct psi_plist *arg = args[0];
163 struct psi_token *tok;
164
165 switch (psi_plist_count(arg)) {
166 case 1:
167 if (psi_plist_get(arg, 0, &tok)) {
168 const char *cpp_search = cpp->search;
169 bool has = psi_cpp_has_include(cpp, tok, PSI_CPP_INCLUDE_NEXT, NULL);
170 cpp->search = cpp_search;
171 return has;
172 }
173 /* no break */
174 default:
175 cpp->parser->error(PSI_DATA(cpp->parser), target, PSI_WARNING,
176 "Erroneous usage of builtin __%s", __FUNCTION__);
177 }
178 return false;
179 }
180
181 static bool has_feature(struct psi_cpp *cpp, struct psi_token *target,
182 struct psi_plist **args, struct psi_plist **res_ptr)
183 {
184 return false;
185 }
186
187 static bool builtin_constant_p(struct psi_cpp *cpp, struct psi_token *target,
188 struct psi_plist **args, struct psi_plist **res_ptr)
189 {
190 /* we want functions, not macros for e.g. htonl() */
191 return false;
192 }
193
194 #define NEW_TOKEN(typ, str, len) \
195 psi_token_init((typ), (str), (len), (target)->col, (target)->line, (target)->file)
196
197 #define ADD_TOKEN(tok) do { \
198 struct psi_token *tok__ = tok; \
199 if (!*res) { \
200 *res = psi_plist_init((psi_plist_dtor) psi_token_free); \
201 } \
202 *res = psi_plist_add(*res, &tok__); \
203 } while (0)
204
205 #define ADD_QUOTED_STRING(buf, len) do { \
206 ADD_TOKEN(NEW_TOKEN(PSI_T_QUOTED_STRING, buf, len)); \
207 } while(0)
208
209 #define ADD_QUOTED_ZSTRING(zs) do { \
210 zend_string *zs_ = zs; \
211 ADD_QUOTED_STRING(zs_->val, zs_->len); \
212 } while (0)
213
214 #define ADD_UNSIGNED_NUMBER(u) do { \
215 char buf[0x20]; \
216 unsigned u_ = u; \
217 size_t len = sprintf(buf, "%u", u_); \
218 struct psi_token *tok_ = NEW_TOKEN(PSI_T_NUMBER, buf, len); \
219 tok_->flags |= PSI_NUMBER_INT | PSI_NUMBER_U; \
220 ADD_TOKEN(tok_); \
221 } while (0)
222
223 static bool BASE_FILE__(struct psi_cpp *cpp, struct psi_token *target,
224 struct psi_plist **args, struct psi_plist **res)
225 {
226 ADD_QUOTED_ZSTRING(cpp->parser->input->file);
227 return true;
228 }
229
230 static bool COUNTER__(struct psi_cpp *cpp, struct psi_token *target,
231 struct psi_plist **args, struct psi_plist **res)
232 {
233 ADD_UNSIGNED_NUMBER(cpp->counter++);
234 return true;
235 }
236
237 static bool FILE__(struct psi_cpp *cpp, struct psi_token *target,
238 struct psi_plist **args, struct psi_plist **res)
239 {
240 ADD_QUOTED_ZSTRING(target->file);
241 return true;
242 }
243
244 static bool INCLUDE_LEVEL__(struct psi_cpp *cpp, struct psi_token *target,
245 struct psi_plist **args, struct psi_plist **res)
246 {
247 ADD_UNSIGNED_NUMBER(cpp->include_level);
248 return true;
249 }
250
251 static bool LINE__(struct psi_cpp *cpp, struct psi_token *target,
252 struct psi_plist **args, struct psi_plist **res)
253 {
254 ADD_UNSIGNED_NUMBER(target->line);
255 return true;
256 }
257
258 static bool TIMESTAMP__(struct psi_cpp *cpp, struct psi_token *target,
259 struct psi_plist **args, struct psi_plist **res)
260 {
261 char *str;
262 #ifdef HAVE_CTIME_R
263 char buf[26];
264 str = ctime_r(&cpp->parser->input->lmod, buf);
265 #else
266 str = ctime(&cpp->parser->input->lmod);
267 #endif
268
269 /* kill EOL */
270 ADD_QUOTED_STRING(str, 24);
271 return true;
272 }
273
274
275 #ifdef __APPLE__
276 #include <libkern/OSByteOrder.h>
277 # define bswap_16(u) _OSSwapInt16(u)
278 # define bswap_32(u) _OSSwapInt32(u)
279 # define bswap_64(u) _OSSwapInt64(u)
280 #elif defined(__FreeBSD__)
281 # include <sys/endian.h>
282 # define bswap_16(u) bswap16(u)
283 # define bswap_32(u) bswap32(u)
284 # define bswap_64(u) bswap64(u)
285 #elif defined(__OpenBSD__)
286 # include <sys/types.h>
287 # define bswap_16(u) swap16(u)
288 # define bswap_32(u) swap32(u)
289 # define bswap_64(u) swap64(u)
290 #elif defined(__NetBSD__)
291 # include <sys/types.h>
292 # include <machine/bswap.h>
293 # define bswap_16(u) bswap16(u)
294 # define bswap_32(u) bswap32(u)
295 # define bswap_64(u) bswap64(u)
296 #else
297 # include <byteswap.h>
298 #endif
299
300 uint16_t psi_swap16(uint16_t u)
301 {
302 return bswap_16(u);
303 }
304
305 uint32_t psi_swap32(uint32_t u)
306 {
307 return bswap_32(u);
308 }
309
310 uint64_t psi_swap64(uint64_t u)
311 {
312 return bswap_64(u);
313 }