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