057e6f2fa7f9407d16deb16af6843934d05ba396
[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 INCLUDE_LEVEL__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
49 static bool TIMESTAMP__(struct psi_cpp *cpp, struct psi_token *target, struct psi_plist **args, struct psi_plist **res);
50
51 static inline struct psi_plist *builtin_sig(token_t typ, ...)
52 {
53 struct psi_plist *sig;
54 size_t n = 0;
55 va_list args;
56
57 if (typ == (token_t) - 1) {
58 return NULL;
59 }
60
61 sig = psi_plist_init((psi_plist_dtor) psi_token_free);
62 va_start(args, typ);
63 while (typ) {
64 char a = 'a' + n++;
65 struct psi_token *arg;
66
67 arg = psi_token_init(typ, &a, 1, 0, 0, zend_empty_string);
68 sig = psi_plist_add(sig, &arg);
69 typ = va_arg(args, token_t);
70 }
71 va_end(args);
72
73 return sig;
74 }
75
76 static void free_builtin(zval *p)
77 {
78 struct psi_builtin *b = Z_PTR_P(p);
79
80 if (b) {
81 zend_string_release(b->name);
82 psi_cpp_macro_decl_free(&b->decl);
83 pefree(b, 1);
84 }
85 }
86
87 PHP_MINIT_FUNCTION(psi_builtin);
88 PHP_MINIT_FUNCTION(psi_builtin)
89 {
90 #define PSI_BUILTIN(builtin, ...) do { \
91 struct psi_builtin entry; \
92 struct psi_plist *sig = builtin_sig(__VA_ARGS__, 0); \
93 struct psi_cpp_macro_decl *decl = psi_cpp_macro_decl_init(sig, NULL, NULL); \
94 decl->token = psi_token_init(PSI_T_NAME, "__" #builtin, sizeof("__" #builtin)-1, \
95 0, 0, zend_empty_string); \
96 entry.name = zend_string_copy(decl->token->text); \
97 entry.func = &builtin; \
98 entry.decl = decl; \
99 zend_hash_add_mem(&psi_builtins, entry.name, &entry, sizeof(entry)); \
100 } while(0)
101
102 zend_hash_init(&psi_builtins, 0, NULL, free_builtin, 1);
103 PSI_BUILTIN(has_include, PSI_T_CPP_HEADER);
104 PSI_BUILTIN(has_include_next, PSI_T_CPP_HEADER);
105 PSI_BUILTIN(has_feature, PSI_T_NAME);
106 PSI_BUILTIN(builtin_constant_p, PSI_T_NAME);
107
108 PSI_BUILTIN(BASE_FILE__, -1);
109 PSI_BUILTIN(COUNTER__, -1);
110 PSI_BUILTIN(INCLUDE_LEVEL__, -1);
111 PSI_BUILTIN(TIMESTAMP__, -1);
112
113 return SUCCESS;
114 }
115
116 PHP_MSHUTDOWN_FUNCTION(psi_builtin);
117 PHP_MSHUTDOWN_FUNCTION(psi_builtin)
118 {
119 zend_hash_destroy(&psi_builtins);
120 return SUCCESS;
121 }
122
123 bool psi_builtin_exists(zend_string *name)
124 {
125 return zend_hash_exists(&psi_builtins, name);
126 }
127
128 struct psi_builtin *psi_builtin_get(zend_string *name)
129 {
130 return zend_hash_find_ptr(&psi_builtins, name);
131 }
132
133 static bool has_include(struct psi_cpp *cpp, struct psi_token *target,
134 struct psi_plist **args, struct psi_plist **res)
135 {
136 struct psi_plist *arg = args[0];
137 struct psi_token *tok;
138
139 switch (psi_plist_count(arg)) {
140 case 1:
141 if (psi_plist_get(arg, 0, &tok)) {
142 const char *cpp_search = cpp->search;
143 bool has = psi_cpp_has_include(cpp, tok, 0, NULL);
144 cpp->search = cpp_search;
145 return has;
146 }
147 /* no break */
148 default:
149 cpp->parser->error(PSI_DATA(cpp->parser), target, PSI_WARNING,
150 "Erroneous usage of builtin __%s", __FUNCTION__);
151 }
152 return false;
153 }
154
155 static bool has_include_next(struct psi_cpp *cpp, struct psi_token *target,
156 struct psi_plist **args, struct psi_plist **res)
157 {
158 struct psi_plist *arg = args[0];
159 struct psi_token *tok;
160
161 switch (psi_plist_count(arg)) {
162 case 1:
163 if (psi_plist_get(arg, 0, &tok)) {
164 const char *cpp_search = cpp->search;
165 bool has = psi_cpp_has_include(cpp, tok, PSI_CPP_INCLUDE_NEXT, NULL);
166 cpp->search = cpp_search;
167 return has;
168 }
169 /* no break */
170 default:
171 cpp->parser->error(PSI_DATA(cpp->parser), target, PSI_WARNING,
172 "Erroneous usage of builtin __%s", __FUNCTION__);
173 }
174 return false;
175 }
176
177 static bool has_feature(struct psi_cpp *cpp, struct psi_token *target,
178 struct psi_plist **args, struct psi_plist **res_ptr)
179 {
180 return false;
181 }
182
183 static bool builtin_constant_p(struct psi_cpp *cpp, struct psi_token *target,
184 struct psi_plist **args, struct psi_plist **res_ptr)
185 {
186 /* we want functions, not macros for e.g. htonl() */
187 return false;
188 }
189
190 #define NEW_TOKEN(typ, str, len) \
191 psi_token_init((typ), (str), (len), (target)->col, (target)->line, (target)->file)
192
193 #define ADD_TOKEN(tok) do { \
194 struct psi_token *tok__ = tok; \
195 if (!*res) { \
196 *res = psi_plist_init((psi_plist_dtor) psi_token_free); \
197 } \
198 *res = psi_plist_add(*res, &tok__); \
199 } while (0)
200
201 #define ADD_QUOTED_STRING(buf, len) do { \
202 ADD_TOKEN(NEW_TOKEN(PSI_T_QUOTED_STRING, buf, len)); \
203 } while(0)
204
205 #define ADD_QUOTED_ZSTRING(zs) do { \
206 zend_string *zs_ = zs; \
207 ADD_QUOTED_STRING(zs_->val, zs_->len); \
208 } while (0)
209
210 #define ADD_UNSIGNED_NUMBER(u) do { \
211 char buf[0x20]; \
212 unsigned u_ = u; \
213 size_t len = sprintf(buf, "%u", u_); \
214 struct psi_token *tok_ = NEW_TOKEN(PSI_T_NUMBER, buf, len); \
215 tok_->flags |= PSI_NUMBER_INT | PSI_NUMBER_U; \
216 ADD_TOKEN(tok_); \
217 } while (0)
218
219 static bool BASE_FILE__(struct psi_cpp *cpp, struct psi_token *target,
220 struct psi_plist **args, struct psi_plist **res)
221 {
222 ADD_QUOTED_ZSTRING(cpp->parser->input->file);
223 return true;
224 }
225
226 static bool COUNTER__(struct psi_cpp *cpp, struct psi_token *target,
227 struct psi_plist **args, struct psi_plist **res)
228 {
229 ADD_UNSIGNED_NUMBER(cpp->counter++);
230 return true;
231 }
232
233 static bool INCLUDE_LEVEL__(struct psi_cpp *cpp, struct psi_token *target,
234 struct psi_plist **args, struct psi_plist **res)
235 {
236 ADD_UNSIGNED_NUMBER(cpp->include_level);
237 return true;
238 }
239
240 static bool TIMESTAMP__(struct psi_cpp *cpp, struct psi_token *target,
241 struct psi_plist **args, struct psi_plist **res)
242 {
243 char *str;
244 #ifdef HAVE_CTIME_R
245 char buf[26];
246 str = ctime_r(&cpp->parser->input->lmod, buf);
247 #else
248 str = ctime(&cpp->parser->input->lmod);
249 #endif
250
251 /* kill EOL */
252 ADD_QUOTED_STRING(str, 24);
253 return true;
254 }
255
256
257 #ifdef __APPLE__
258 #include <libkern/OSByteOrder.h>
259 # define bswap_16(u) _OSSwapInt16(u)
260 # define bswap_32(u) _OSSwapInt32(u)
261 # define bswap_64(u) _OSSwapInt64(u)
262 #elif defined(__FreeBSD__)
263 # include <sys/endian.h>
264 # define bswap_16(u) bswap16(u)
265 # define bswap_32(u) bswap32(u)
266 # define bswap_64(u) bswap64(u)
267 #elif defined(__OpenBSD__)
268 # include <sys/types.h>
269 # define bswap_16(u) swap16(u)
270 # define bswap_32(u) swap32(u)
271 # define bswap_64(u) swap64(u)
272 #elif defined(__NetBSD__)
273 # include <sys/types.h>
274 # include <machine/bswap.h>
275 # define bswap_16(u) bswap16(u)
276 # define bswap_32(u) bswap32(u)
277 # define bswap_64(u) bswap64(u)
278 #else
279 # include <byteswap.h>
280 #endif
281
282 uint16_t psi_swap16(uint16_t u)
283 {
284 return bswap_16(u);
285 }
286
287 uint32_t psi_swap32(uint32_t u)
288 {
289 return bswap_32(u);
290 }
291
292 uint64_t psi_swap64(uint64_t u)
293 {
294 return bswap_64(u);
295 }