1 /*
2 +----------------------------------------------------------------------+
3 | Zend Engine |
4 +----------------------------------------------------------------------+
5 | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 2.00 of the Zend license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.zend.com/license/2_00.txt. |
11 | If you did not receive a copy of the Zend license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@zend.com so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Benjamin Eberlei <kontakt@beberlei.de> |
16 | Martin Schröder <m.schroeder2007@gmail.com> |
17 +----------------------------------------------------------------------+
18 */
19
20 #include "zend.h"
21 #include "zend_API.h"
22 #include "zend_attributes.h"
23 #include "zend_attributes_arginfo.h"
24 #include "zend_smart_str.h"
25
26 ZEND_API zend_class_entry *zend_ce_attribute;
27 ZEND_API zend_class_entry *zend_ce_return_type_will_change_attribute;
28
29 static HashTable internal_attributes;
30
validate_attribute(zend_attribute * attr,uint32_t target,zend_class_entry * scope)31 void validate_attribute(zend_attribute *attr, uint32_t target, zend_class_entry *scope)
32 {
33 // TODO: More proper signature validation: Too many args, incorrect arg names.
34 if (attr->argc > 0) {
35 zval flags;
36
37 /* As this is run in the middle of compilation, fetch the attribute value without
38 * specifying a scope. The class is not fully linked yet, and we may seen an
39 * inconsistent state. */
40 if (FAILURE == zend_get_attribute_value(&flags, attr, 0, NULL)) {
41 return;
42 }
43
44 if (Z_TYPE(flags) != IS_LONG) {
45 zend_error_noreturn(E_ERROR,
46 "Attribute::__construct(): Argument #1 ($flags) must be of type int, %s given",
47 zend_zval_type_name(&flags)
48 );
49 }
50
51 if (Z_LVAL(flags) & ~ZEND_ATTRIBUTE_FLAGS) {
52 zend_error_noreturn(E_ERROR, "Invalid attribute flags specified");
53 }
54
55 zval_ptr_dtor(&flags);
56 }
57 }
58
ZEND_METHOD(Attribute,__construct)59 ZEND_METHOD(Attribute, __construct)
60 {
61 zend_long flags = ZEND_ATTRIBUTE_TARGET_ALL;
62
63 ZEND_PARSE_PARAMETERS_START(0, 1)
64 Z_PARAM_OPTIONAL
65 Z_PARAM_LONG(flags)
66 ZEND_PARSE_PARAMETERS_END();
67
68 ZVAL_LONG(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), flags);
69 }
70
ZEND_METHOD(ReturnTypeWillChange,__construct)71 ZEND_METHOD(ReturnTypeWillChange, __construct)
72 {
73 ZEND_PARSE_PARAMETERS_NONE();
74 }
75
get_attribute(HashTable * attributes,zend_string * lcname,uint32_t offset)76 static zend_attribute *get_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset)
77 {
78 if (attributes) {
79 zend_attribute *attr;
80
81 ZEND_HASH_FOREACH_PTR(attributes, attr) {
82 if (attr->offset == offset && zend_string_equals(attr->lcname, lcname)) {
83 return attr;
84 }
85 } ZEND_HASH_FOREACH_END();
86 }
87
88 return NULL;
89 }
90
get_attribute_str(HashTable * attributes,const char * str,size_t len,uint32_t offset)91 static zend_attribute *get_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset)
92 {
93 if (attributes) {
94 zend_attribute *attr;
95
96 ZEND_HASH_FOREACH_PTR(attributes, attr) {
97 if (attr->offset == offset && ZSTR_LEN(attr->lcname) == len) {
98 if (0 == memcmp(ZSTR_VAL(attr->lcname), str, len)) {
99 return attr;
100 }
101 }
102 } ZEND_HASH_FOREACH_END();
103 }
104
105 return NULL;
106 }
107
zend_get_attribute(HashTable * attributes,zend_string * lcname)108 ZEND_API zend_attribute *zend_get_attribute(HashTable *attributes, zend_string *lcname)
109 {
110 return get_attribute(attributes, lcname, 0);
111 }
112
zend_get_attribute_str(HashTable * attributes,const char * str,size_t len)113 ZEND_API zend_attribute *zend_get_attribute_str(HashTable *attributes, const char *str, size_t len)
114 {
115 return get_attribute_str(attributes, str, len, 0);
116 }
117
zend_get_parameter_attribute(HashTable * attributes,zend_string * lcname,uint32_t offset)118 ZEND_API zend_attribute *zend_get_parameter_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset)
119 {
120 return get_attribute(attributes, lcname, offset + 1);
121 }
122
zend_get_parameter_attribute_str(HashTable * attributes,const char * str,size_t len,uint32_t offset)123 ZEND_API zend_attribute *zend_get_parameter_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset)
124 {
125 return get_attribute_str(attributes, str, len, offset + 1);
126 }
127
zend_get_attribute_value(zval * ret,zend_attribute * attr,uint32_t i,zend_class_entry * scope)128 ZEND_API zend_result zend_get_attribute_value(zval *ret, zend_attribute *attr, uint32_t i, zend_class_entry *scope)
129 {
130 if (i >= attr->argc) {
131 return FAILURE;
132 }
133
134 ZVAL_COPY_OR_DUP(ret, &attr->args[i].value);
135
136 if (Z_TYPE_P(ret) == IS_CONSTANT_AST) {
137 if (SUCCESS != zval_update_constant_ex(ret, scope)) {
138 zval_ptr_dtor(ret);
139 return FAILURE;
140 }
141 }
142
143 return SUCCESS;
144 }
145
146 static const char *target_names[] = {
147 "class",
148 "function",
149 "method",
150 "property",
151 "class constant",
152 "parameter"
153 };
154
zend_get_attribute_target_names(uint32_t flags)155 ZEND_API zend_string *zend_get_attribute_target_names(uint32_t flags)
156 {
157 smart_str str = { 0 };
158
159 for (uint32_t i = 0; i < (sizeof(target_names) / sizeof(char *)); i++) {
160 if (flags & (1 << i)) {
161 if (smart_str_get_len(&str)) {
162 smart_str_appends(&str, ", ");
163 }
164
165 smart_str_appends(&str, target_names[i]);
166 }
167 }
168
169 return smart_str_extract(&str);
170 }
171
zend_is_attribute_repeated(HashTable * attributes,zend_attribute * attr)172 ZEND_API bool zend_is_attribute_repeated(HashTable *attributes, zend_attribute *attr)
173 {
174 zend_attribute *other;
175
176 ZEND_HASH_FOREACH_PTR(attributes, other) {
177 if (other != attr && other->offset == attr->offset) {
178 if (zend_string_equals(other->lcname, attr->lcname)) {
179 return 1;
180 }
181 }
182 } ZEND_HASH_FOREACH_END();
183
184 return 0;
185 }
186
attr_free(zval * v)187 static void attr_free(zval *v)
188 {
189 zend_attribute *attr = Z_PTR_P(v);
190 bool persistent = attr->flags & ZEND_ATTRIBUTE_PERSISTENT;
191
192 zend_string_release(attr->name);
193 zend_string_release(attr->lcname);
194
195 for (uint32_t i = 0; i < attr->argc; i++) {
196 if (attr->args[i].name) {
197 zend_string_release(attr->args[i].name);
198 }
199 if (persistent) {
200 zval_internal_ptr_dtor(&attr->args[i].value);
201 } else {
202 zval_ptr_dtor(&attr->args[i].value);
203 }
204 }
205
206 pefree(attr, persistent);
207 }
208
zend_add_attribute(HashTable ** attributes,zend_string * name,uint32_t argc,uint32_t flags,uint32_t offset,uint32_t lineno)209 ZEND_API zend_attribute *zend_add_attribute(HashTable **attributes, zend_string *name, uint32_t argc, uint32_t flags, uint32_t offset, uint32_t lineno)
210 {
211 bool persistent = flags & ZEND_ATTRIBUTE_PERSISTENT;
212 if (*attributes == NULL) {
213 *attributes = pemalloc(sizeof(HashTable), persistent);
214 zend_hash_init(*attributes, 8, NULL, attr_free, persistent);
215 }
216
217 zend_attribute *attr = pemalloc(ZEND_ATTRIBUTE_SIZE(argc), persistent);
218
219 if (persistent == ((GC_FLAGS(name) & IS_STR_PERSISTENT) != 0)) {
220 attr->name = zend_string_copy(name);
221 } else {
222 attr->name = zend_string_dup(name, persistent);
223 }
224
225 attr->lcname = zend_string_tolower_ex(attr->name, persistent);
226 attr->flags = flags;
227 attr->lineno = lineno;
228 attr->offset = offset;
229 attr->argc = argc;
230
231 /* Initialize arguments to avoid partial initialization in case of fatal errors. */
232 for (uint32_t i = 0; i < argc; i++) {
233 attr->args[i].name = NULL;
234 ZVAL_UNDEF(&attr->args[i].value);
235 }
236
237 zend_hash_next_index_insert_ptr(*attributes, attr);
238
239 return attr;
240 }
241
free_internal_attribute(zval * v)242 static void free_internal_attribute(zval *v)
243 {
244 pefree(Z_PTR_P(v), 1);
245 }
246
zend_internal_attribute_register(zend_class_entry * ce,uint32_t flags)247 ZEND_API zend_internal_attribute *zend_internal_attribute_register(zend_class_entry *ce, uint32_t flags)
248 {
249 zend_internal_attribute *internal_attr;
250
251 if (ce->type != ZEND_INTERNAL_CLASS) {
252 zend_error_noreturn(E_ERROR, "Only internal classes can be registered as compiler attribute");
253 }
254
255 internal_attr = pemalloc(sizeof(zend_internal_attribute), 1);
256 internal_attr->ce = ce;
257 internal_attr->flags = flags;
258 internal_attr->validator = NULL;
259
260 zend_string *lcname = zend_string_tolower_ex(ce->name, 1);
261
262 zend_hash_update_ptr(&internal_attributes, lcname, internal_attr);
263 zend_attribute *attr = zend_add_class_attribute(ce, zend_ce_attribute->name, 1);
264 ZVAL_LONG(&attr->args[0].value, flags);
265 zend_string_release(lcname);
266
267 return internal_attr;
268 }
269
zend_internal_attribute_get(zend_string * lcname)270 ZEND_API zend_internal_attribute *zend_internal_attribute_get(zend_string *lcname)
271 {
272 return zend_hash_find_ptr(&internal_attributes, lcname);
273 }
274
zend_register_attribute_ce(void)275 void zend_register_attribute_ce(void)
276 {
277 zend_internal_attribute *attr;
278
279 zend_hash_init(&internal_attributes, 8, NULL, free_internal_attribute, 1);
280
281 zend_ce_attribute = register_class_Attribute();
282 attr = zend_internal_attribute_register(zend_ce_attribute, ZEND_ATTRIBUTE_TARGET_CLASS);
283 attr->validator = validate_attribute;
284
285 zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("TARGET_CLASS"), ZEND_ATTRIBUTE_TARGET_CLASS);
286 zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("TARGET_FUNCTION"), ZEND_ATTRIBUTE_TARGET_FUNCTION);
287 zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("TARGET_METHOD"), ZEND_ATTRIBUTE_TARGET_METHOD);
288 zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("TARGET_PROPERTY"), ZEND_ATTRIBUTE_TARGET_PROPERTY);
289 zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("TARGET_CLASS_CONSTANT"), ZEND_ATTRIBUTE_TARGET_CLASS_CONST);
290 zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("TARGET_PARAMETER"), ZEND_ATTRIBUTE_TARGET_PARAMETER);
291 zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("TARGET_ALL"), ZEND_ATTRIBUTE_TARGET_ALL);
292 zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("IS_REPEATABLE"), ZEND_ATTRIBUTE_IS_REPEATABLE);
293
294 zend_ce_return_type_will_change_attribute = register_class_ReturnTypeWillChange();
295 zend_internal_attribute_register(zend_ce_return_type_will_change_attribute, ZEND_ATTRIBUTE_TARGET_METHOD);
296 }
297
zend_attributes_shutdown(void)298 void zend_attributes_shutdown(void)
299 {
300 zend_hash_destroy(&internal_attributes);
301 }
302