/src/php-src/Zend/zend_attributes.c
Line | Count | Source |
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_exceptions.h" |
25 | | #include "zend_smart_str.h" |
26 | | |
27 | | ZEND_API zend_class_entry *zend_ce_attribute; |
28 | | ZEND_API zend_class_entry *zend_ce_return_type_will_change_attribute; |
29 | | ZEND_API zend_class_entry *zend_ce_allow_dynamic_properties; |
30 | | ZEND_API zend_class_entry *zend_ce_sensitive_parameter; |
31 | | ZEND_API zend_class_entry *zend_ce_sensitive_parameter_value; |
32 | | ZEND_API zend_class_entry *zend_ce_override; |
33 | | ZEND_API zend_class_entry *zend_ce_deprecated; |
34 | | ZEND_API zend_class_entry *zend_ce_nodiscard; |
35 | | ZEND_API zend_class_entry *zend_ce_delayed_target_validation; |
36 | | |
37 | | static zend_object_handlers attributes_object_handlers_sensitive_parameter_value; |
38 | | |
39 | | static HashTable internal_attributes; |
40 | | |
41 | | uint32_t zend_attribute_attribute_get_flags(zend_attribute *attr, zend_class_entry *scope) |
42 | 388 | { |
43 | | // TODO: More proper signature validation: Too many args, incorrect arg names. |
44 | 388 | if (attr->argc > 0) { |
45 | 311 | zval flags; |
46 | | |
47 | 311 | if (FAILURE == zend_get_attribute_value(&flags, attr, 0, scope)) { |
48 | 5 | ZEND_ASSERT(EG(exception)); |
49 | 5 | return 0; |
50 | 5 | } |
51 | | |
52 | 306 | if (Z_TYPE(flags) != IS_LONG) { |
53 | 6 | zend_throw_error(NULL, |
54 | 6 | "Attribute::__construct(): Argument #1 ($flags) must be of type int, %s given", |
55 | 6 | zend_zval_value_name(&flags) |
56 | 6 | ); |
57 | 6 | zval_ptr_dtor(&flags); |
58 | 6 | return 0; |
59 | 6 | } |
60 | | |
61 | 300 | uint32_t flags_l = Z_LVAL(flags); |
62 | 300 | if (flags_l & ~ZEND_ATTRIBUTE_FLAGS) { |
63 | 5 | zend_throw_error(NULL, "Invalid attribute flags specified"); |
64 | 5 | return 0; |
65 | 5 | } |
66 | | |
67 | 295 | return flags_l; |
68 | 300 | } |
69 | | |
70 | 77 | return ZEND_ATTRIBUTE_TARGET_ALL; |
71 | 388 | } |
72 | | |
73 | | static zend_string *validate_allow_dynamic_properties( |
74 | | zend_attribute *attr, uint32_t target, zend_class_entry *scope) |
75 | 736 | { |
76 | 736 | ZEND_ASSERT(scope != NULL); |
77 | 736 | const char *msg = NULL; |
78 | 736 | if (scope->ce_flags & ZEND_ACC_TRAIT) { |
79 | 59 | msg = "Cannot apply #[\\AllowDynamicProperties] to trait %s"; |
80 | 677 | } else if (scope->ce_flags & ZEND_ACC_INTERFACE) { |
81 | 76 | msg = "Cannot apply #[\\AllowDynamicProperties] to interface %s"; |
82 | 601 | } else if (scope->ce_flags & ZEND_ACC_READONLY_CLASS) { |
83 | 76 | msg = "Cannot apply #[\\AllowDynamicProperties] to readonly class %s"; |
84 | 525 | } else if (scope->ce_flags & ZEND_ACC_ENUM) { |
85 | 19 | msg = "Cannot apply #[\\AllowDynamicProperties] to enum %s"; |
86 | 19 | } |
87 | 736 | if (msg != NULL) { |
88 | 230 | return zend_strpprintf(0, msg, ZSTR_VAL(scope->name)); |
89 | 230 | } |
90 | 506 | scope->ce_flags |= ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES; |
91 | 506 | return NULL; |
92 | 736 | } |
93 | | |
94 | | static zend_string *validate_attribute( |
95 | | zend_attribute *attr, uint32_t target, zend_class_entry *scope) |
96 | 282 | { |
97 | 282 | const char *msg = NULL; |
98 | 282 | if (scope->ce_flags & ZEND_ACC_TRAIT) { |
99 | 16 | msg = "Cannot apply #[\\Attribute] to trait %s"; |
100 | 266 | } else if (scope->ce_flags & ZEND_ACC_INTERFACE) { |
101 | 12 | msg = "Cannot apply #[\\Attribute] to interface %s"; |
102 | 254 | } else if (scope->ce_flags & ZEND_ACC_ENUM) { |
103 | 17 | msg = "Cannot apply #[\\Attribute] to enum %s"; |
104 | 237 | } else if (scope->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) { |
105 | 14 | msg = "Cannot apply #[\\Attribute] to abstract class %s"; |
106 | 14 | } |
107 | 282 | if (msg != NULL) { |
108 | 59 | return zend_strpprintf(0, msg, ZSTR_VAL(scope->name)); |
109 | 59 | } |
110 | 223 | return NULL; |
111 | 282 | } |
112 | | |
113 | | static zend_string *validate_deprecated( |
114 | | zend_attribute *attr, |
115 | | uint32_t target, |
116 | | zend_class_entry *scope |
117 | 581 | ) { |
118 | 581 | if (target != ZEND_ATTRIBUTE_TARGET_CLASS) { |
119 | | /* Being used for a method or something, validation does not apply */ |
120 | 482 | return NULL; |
121 | 482 | } |
122 | 99 | if (!(scope->ce_flags & ZEND_ACC_TRAIT)) { |
123 | 41 | const char *type = zend_get_object_type_case(scope, false); |
124 | 41 | return zend_strpprintf(0, "Cannot apply #[\\Deprecated] to %s %s", type, ZSTR_VAL(scope->name)); |
125 | 41 | } |
126 | | |
127 | 58 | scope->ce_flags |= ZEND_ACC_DEPRECATED; |
128 | 58 | return NULL; |
129 | | |
130 | 99 | } |
131 | | |
132 | | ZEND_METHOD(Attribute, __construct) |
133 | 16 | { |
134 | 16 | zend_long flags = ZEND_ATTRIBUTE_TARGET_ALL; |
135 | | |
136 | 48 | ZEND_PARSE_PARAMETERS_START(0, 1) |
137 | 48 | Z_PARAM_OPTIONAL |
138 | 54 | Z_PARAM_LONG(flags) |
139 | 16 | ZEND_PARSE_PARAMETERS_END(); |
140 | | |
141 | 16 | ZVAL_LONG(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), flags); |
142 | 16 | } |
143 | | |
144 | | ZEND_METHOD(ReturnTypeWillChange, __construct) |
145 | 5 | { |
146 | 5 | ZEND_PARSE_PARAMETERS_NONE(); |
147 | 5 | } |
148 | | |
149 | | ZEND_METHOD(AllowDynamicProperties, __construct) |
150 | 10 | { |
151 | 10 | ZEND_PARSE_PARAMETERS_NONE(); |
152 | 10 | } |
153 | | |
154 | | ZEND_METHOD(SensitiveParameter, __construct) |
155 | 5 | { |
156 | 5 | ZEND_PARSE_PARAMETERS_NONE(); |
157 | 5 | } |
158 | | |
159 | | ZEND_METHOD(SensitiveParameterValue, __construct) |
160 | 392 | { |
161 | 392 | zval *value; |
162 | | |
163 | 1.17k | ZEND_PARSE_PARAMETERS_START(1, 1) |
164 | 1.54k | Z_PARAM_ZVAL(value) |
165 | 1.54k | ZEND_PARSE_PARAMETERS_END(); |
166 | | |
167 | 387 | zend_update_property_ex(zend_ce_sensitive_parameter_value, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_VALUE), value); |
168 | 387 | } |
169 | | |
170 | | ZEND_METHOD(SensitiveParameterValue, getValue) |
171 | 48 | { |
172 | 48 | ZEND_PARSE_PARAMETERS_NONE(); |
173 | | |
174 | 48 | ZVAL_COPY(return_value, OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0)); |
175 | 48 | } |
176 | | |
177 | | ZEND_METHOD(SensitiveParameterValue, __debugInfo) |
178 | 0 | { |
179 | 0 | ZEND_PARSE_PARAMETERS_NONE(); |
180 | | |
181 | 0 | RETURN_EMPTY_ARRAY(); |
182 | 0 | } |
183 | | |
184 | | static HashTable *attributes_sensitive_parameter_value_get_properties_for(zend_object *zobj, zend_prop_purpose purpose) |
185 | 210 | { |
186 | 210 | return NULL; |
187 | 210 | } |
188 | | |
189 | | ZEND_METHOD(Override, __construct) |
190 | 5 | { |
191 | 5 | ZEND_PARSE_PARAMETERS_NONE(); |
192 | 5 | } |
193 | | |
194 | | ZEND_METHOD(Deprecated, __construct) |
195 | 453 | { |
196 | 453 | zend_string *message = NULL; |
197 | 453 | zend_string *since = NULL; |
198 | 453 | zval value; |
199 | | |
200 | 1.35k | ZEND_PARSE_PARAMETERS_START(0, 2) |
201 | 1.35k | Z_PARAM_OPTIONAL |
202 | 1.80k | Z_PARAM_STR_OR_NULL(message) |
203 | 1.79k | Z_PARAM_STR_OR_NULL(since) |
204 | 453 | ZEND_PARSE_PARAMETERS_END(); |
205 | | |
206 | 433 | if (message) { |
207 | 346 | ZVAL_STR(&value, message); |
208 | 346 | } else { |
209 | 87 | ZVAL_NULL(&value); |
210 | 87 | } |
211 | 433 | zend_update_property_ex(zend_ce_deprecated, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_MESSAGE), &value); |
212 | | |
213 | | /* The assignment might fail due to 'readonly'. */ |
214 | 433 | if (UNEXPECTED(EG(exception))) { |
215 | 6 | RETURN_THROWS(); |
216 | 6 | } |
217 | | |
218 | 427 | if (since) { |
219 | 253 | ZVAL_STR(&value, since); |
220 | 253 | } else { |
221 | 174 | ZVAL_NULL(&value); |
222 | 174 | } |
223 | 427 | zend_update_property_ex(zend_ce_deprecated, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_SINCE), &value); |
224 | | |
225 | | /* The assignment might fail due to 'readonly'. */ |
226 | 427 | if (UNEXPECTED(EG(exception))) { |
227 | 0 | RETURN_THROWS(); |
228 | 0 | } |
229 | 427 | } |
230 | | |
231 | | static zend_string *validate_nodiscard( |
232 | | zend_attribute *attr, uint32_t target, zend_class_entry *scope) |
233 | 294 | { |
234 | 294 | ZEND_ASSERT(CG(in_compilation)); |
235 | 294 | const zend_string *prop_info_name = CG(context).active_property_info_name; |
236 | 294 | if (prop_info_name != NULL) { |
237 | | // Applied to a hook |
238 | 25 | return ZSTR_INIT_LITERAL("#[\\NoDiscard] is not supported for property hooks", 0); |
239 | 25 | } |
240 | 269 | zend_op_array *op_array = CG(active_op_array); |
241 | 269 | op_array->fn_flags |= ZEND_ACC_NODISCARD; |
242 | 269 | return NULL; |
243 | 294 | } |
244 | | |
245 | | ZEND_METHOD(NoDiscard, __construct) |
246 | 53 | { |
247 | 53 | zend_string *message = NULL; |
248 | 53 | zval value; |
249 | | |
250 | 159 | ZEND_PARSE_PARAMETERS_START(0, 1) |
251 | 159 | Z_PARAM_OPTIONAL |
252 | 202 | Z_PARAM_STR_OR_NULL(message) |
253 | 53 | ZEND_PARSE_PARAMETERS_END(); |
254 | | |
255 | 47 | if (message) { |
256 | 42 | ZVAL_STR(&value, message); |
257 | 42 | } else { |
258 | 5 | ZVAL_NULL(&value); |
259 | 5 | } |
260 | 47 | zend_update_property_ex(zend_ce_nodiscard, Z_OBJ_P(ZEND_THIS), ZSTR_KNOWN(ZEND_STR_MESSAGE), &value); |
261 | | |
262 | | /* The assignment might fail due to 'readonly'. */ |
263 | 47 | if (UNEXPECTED(EG(exception))) { |
264 | 6 | RETURN_THROWS(); |
265 | 6 | } |
266 | 47 | } |
267 | | |
268 | | static zend_attribute *get_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset) |
269 | 0 | { |
270 | 0 | if (attributes) { |
271 | 0 | zend_attribute *attr; |
272 | |
|
273 | 0 | ZEND_HASH_PACKED_FOREACH_PTR(attributes, attr) { |
274 | 0 | if (attr->offset == offset && zend_string_equals(attr->lcname, lcname)) { |
275 | 0 | return attr; |
276 | 0 | } |
277 | 0 | } ZEND_HASH_FOREACH_END(); |
278 | 0 | } |
279 | | |
280 | 0 | return NULL; |
281 | 0 | } |
282 | | |
283 | | static zend_attribute *get_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset) |
284 | 2.99M | { |
285 | 2.99M | if (attributes) { |
286 | 1.88M | zend_attribute *attr; |
287 | | |
288 | 15.0M | ZEND_HASH_PACKED_FOREACH_PTR(attributes, attr) { |
289 | 15.0M | if (attr->offset == offset && zend_string_equals_cstr(attr->lcname, str, len)) { |
290 | 3.79k | return attr; |
291 | 3.79k | } |
292 | 15.0M | } ZEND_HASH_FOREACH_END(); |
293 | 1.88M | } |
294 | | |
295 | 2.98M | return NULL; |
296 | 2.99M | } |
297 | | |
298 | | ZEND_API zend_attribute *zend_get_attribute(HashTable *attributes, zend_string *lcname) |
299 | 0 | { |
300 | 0 | return get_attribute(attributes, lcname, 0); |
301 | 0 | } |
302 | | |
303 | | ZEND_API zend_attribute *zend_get_attribute_str(HashTable *attributes, const char *str, size_t len) |
304 | 1.82M | { |
305 | 1.82M | return get_attribute_str(attributes, str, len, 0); |
306 | 1.82M | } |
307 | | |
308 | | ZEND_API zend_attribute *zend_get_parameter_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset) |
309 | 0 | { |
310 | 0 | return get_attribute(attributes, lcname, offset + 1); |
311 | 0 | } |
312 | | |
313 | | ZEND_API zend_attribute *zend_get_parameter_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset) |
314 | 1.16M | { |
315 | 1.16M | return get_attribute_str(attributes, str, len, offset + 1); |
316 | 1.16M | } |
317 | | |
318 | | ZEND_API zend_result zend_get_attribute_value(zval *ret, zend_attribute *attr, uint32_t i, zend_class_entry *scope) |
319 | 1.80k | { |
320 | 1.80k | if (i >= attr->argc) { |
321 | 0 | return FAILURE; |
322 | 0 | } |
323 | | |
324 | 1.80k | ZVAL_COPY_OR_DUP(ret, &attr->args[i].value); |
325 | | |
326 | 1.80k | if (Z_TYPE_P(ret) == IS_CONSTANT_AST) { |
327 | 470 | if (SUCCESS != zval_update_constant_ex(ret, scope)) { |
328 | 36 | zval_ptr_dtor(ret); |
329 | 36 | return FAILURE; |
330 | 36 | } |
331 | 470 | } |
332 | | |
333 | 1.76k | return SUCCESS; |
334 | 1.80k | } |
335 | | |
336 | | ZEND_API zend_result zend_get_attribute_object(zval *obj, zend_class_entry *attribute_ce, zend_attribute *attribute_data, zend_class_entry *scope, zend_string *filename) |
337 | 690 | { |
338 | 690 | zend_execute_data *call = NULL; |
339 | | |
340 | 690 | if (filename) { |
341 | | /* Set up dummy call frame that makes it look like the attribute was invoked |
342 | | * from where it occurs in the code. */ |
343 | 226 | zend_function dummy_func; |
344 | 226 | zend_op *opline; |
345 | | |
346 | 226 | memset(&dummy_func, 0, sizeof(zend_function)); |
347 | | |
348 | 226 | call = zend_vm_stack_push_call_frame_ex( |
349 | 226 | ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_execute_data), sizeof(zval)) + |
350 | 226 | ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op), sizeof(zval)) + |
351 | 226 | ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_function), sizeof(zval)), |
352 | 226 | 0, &dummy_func, 0, NULL); |
353 | | |
354 | 226 | opline = (zend_op*)(call + 1); |
355 | 226 | memset(opline, 0, sizeof(zend_op)); |
356 | 226 | opline->opcode = ZEND_DO_FCALL; |
357 | 226 | opline->lineno = attribute_data->lineno; |
358 | | |
359 | 226 | call->opline = opline; |
360 | 226 | call->call = NULL; |
361 | 226 | call->return_value = NULL; |
362 | 226 | call->func = (zend_function*)(call->opline + 1); |
363 | 226 | call->prev_execute_data = EG(current_execute_data); |
364 | | |
365 | 226 | memset(call->func, 0, sizeof(zend_function)); |
366 | 226 | call->func->type = ZEND_USER_FUNCTION; |
367 | 226 | call->func->op_array.fn_flags = |
368 | 226 | attribute_data->flags & ZEND_ATTRIBUTE_STRICT_TYPES ? ZEND_ACC_STRICT_TYPES : 0; |
369 | 226 | call->func->op_array.fn_flags |= ZEND_ACC_CALL_VIA_TRAMPOLINE; |
370 | 226 | call->func->op_array.filename = filename; |
371 | | |
372 | 226 | EG(current_execute_data) = call; |
373 | 226 | } |
374 | | |
375 | 690 | zval *args = NULL; |
376 | 690 | HashTable *named_params = NULL; |
377 | | |
378 | 690 | zend_result result = FAILURE; |
379 | | |
380 | 690 | uint32_t argc = 0; |
381 | 690 | if (attribute_data->argc) { |
382 | 592 | args = emalloc(attribute_data->argc * sizeof(zval)); |
383 | | |
384 | 1.36k | for (uint32_t i = 0; i < attribute_data->argc; i++) { |
385 | 779 | zval val; |
386 | 779 | if (FAILURE == zend_get_attribute_value(&val, attribute_data, i, scope)) { |
387 | 11 | result = FAILURE; |
388 | 11 | goto out; |
389 | 11 | } |
390 | 768 | if (attribute_data->args[i].name) { |
391 | 454 | if (!named_params) { |
392 | 292 | named_params = zend_new_array(0); |
393 | 292 | } |
394 | 454 | zend_hash_add_new(named_params, attribute_data->args[i].name, &val); |
395 | 454 | } else { |
396 | 314 | ZVAL_COPY_VALUE(&args[i], &val); |
397 | 314 | argc++; |
398 | 314 | } |
399 | 768 | } |
400 | 592 | } |
401 | | |
402 | 679 | result = object_init_with_constructor(obj, attribute_ce, argc, args, named_params); |
403 | | |
404 | 690 | out: |
405 | 1.00k | for (uint32_t i = 0; i < argc; i++) { |
406 | 314 | zval_ptr_dtor(&args[i]); |
407 | 314 | } |
408 | | |
409 | 690 | efree(args); |
410 | | |
411 | 690 | if (named_params) { |
412 | 292 | zend_array_destroy(named_params); |
413 | 292 | } |
414 | | |
415 | 690 | if (filename) { |
416 | 226 | EG(current_execute_data) = call->prev_execute_data; |
417 | 226 | zend_vm_stack_free_call_frame(call); |
418 | 226 | } |
419 | | |
420 | 690 | return result; |
421 | 679 | } |
422 | | |
423 | | static const char *target_names[] = { |
424 | | "class", |
425 | | "function", |
426 | | "method", |
427 | | "property", |
428 | | "class constant", |
429 | | "parameter", |
430 | | "constant" |
431 | | }; |
432 | | |
433 | | ZEND_API zend_string *zend_get_attribute_target_names(uint32_t flags) |
434 | 220 | { |
435 | 220 | smart_str str = { 0 }; |
436 | | |
437 | 1.76k | for (uint32_t i = 0; i < (sizeof(target_names) / sizeof(char *)); i++) { |
438 | 1.54k | if (flags & (1 << i)) { |
439 | 267 | if (smart_str_get_len(&str)) { |
440 | 47 | smart_str_appends(&str, ", "); |
441 | 47 | } |
442 | | |
443 | 267 | smart_str_appends(&str, target_names[i]); |
444 | 267 | } |
445 | 1.54k | } |
446 | | |
447 | 220 | return smart_str_extract(&str); |
448 | 220 | } |
449 | | |
450 | | ZEND_API bool zend_is_attribute_repeated(HashTable *attributes, zend_attribute *attr) |
451 | 4.10k | { |
452 | 4.10k | zend_attribute *other; |
453 | | |
454 | 20.7k | ZEND_HASH_PACKED_FOREACH_PTR(attributes, other) { |
455 | 20.7k | if (other != attr && other->offset == attr->offset) { |
456 | 2.04k | if (zend_string_equals(other->lcname, attr->lcname)) { |
457 | 46 | return 1; |
458 | 46 | } |
459 | 2.04k | } |
460 | 20.7k | } ZEND_HASH_FOREACH_END(); |
461 | | |
462 | 4.06k | return 0; |
463 | 4.10k | } |
464 | | |
465 | | static void attr_free(zval *v) |
466 | 752k | { |
467 | 752k | zend_attribute *attr = Z_PTR_P(v); |
468 | 752k | bool persistent = attr->flags & ZEND_ATTRIBUTE_PERSISTENT; |
469 | | |
470 | 752k | zend_string_release(attr->name); |
471 | 752k | zend_string_release(attr->lcname); |
472 | 752k | if (attr->validation_error != NULL) { |
473 | 186 | zend_string_release(attr->validation_error); |
474 | 186 | } |
475 | | |
476 | 794k | for (uint32_t i = 0; i < attr->argc; i++) { |
477 | 42.0k | if (attr->args[i].name) { |
478 | 2.49k | zend_string_release(attr->args[i].name); |
479 | 2.49k | } |
480 | 42.0k | if (persistent) { |
481 | 0 | zval_internal_ptr_dtor(&attr->args[i].value); |
482 | 42.0k | } else { |
483 | 42.0k | zval_ptr_dtor(&attr->args[i].value); |
484 | 42.0k | } |
485 | 42.0k | } |
486 | | |
487 | 752k | pefree(attr, persistent); |
488 | 752k | } |
489 | | |
490 | | ZEND_API zend_attribute *zend_add_attribute(HashTable **attributes, zend_string *name, uint32_t argc, uint32_t flags, uint32_t offset, uint32_t lineno) |
491 | 1.63M | { |
492 | 1.63M | bool persistent = flags & ZEND_ATTRIBUTE_PERSISTENT; |
493 | 1.63M | if (*attributes == NULL) { |
494 | 612k | *attributes = pemalloc(sizeof(HashTable), persistent); |
495 | 612k | zend_hash_init(*attributes, 8, NULL, attr_free, persistent); |
496 | 612k | } |
497 | | |
498 | 1.63M | zend_attribute *attr = pemalloc(ZEND_ATTRIBUTE_SIZE(argc), persistent); |
499 | | |
500 | 1.63M | if (persistent == ((GC_FLAGS(name) & IS_STR_PERSISTENT) != 0)) { |
501 | 1.63M | attr->name = zend_string_copy(name); |
502 | 1.63M | } else { |
503 | 0 | attr->name = zend_string_dup(name, persistent); |
504 | 0 | } |
505 | | |
506 | 1.63M | attr->lcname = zend_string_tolower_ex(attr->name, persistent); |
507 | 1.63M | attr->validation_error = NULL; |
508 | 1.63M | attr->flags = flags; |
509 | 1.63M | attr->lineno = lineno; |
510 | 1.63M | attr->offset = offset; |
511 | 1.63M | attr->argc = argc; |
512 | | |
513 | | /* Initialize arguments to avoid partial initialization in case of fatal errors. */ |
514 | 1.70M | for (uint32_t i = 0; i < argc; i++) { |
515 | 72.2k | attr->args[i].name = NULL; |
516 | 72.2k | ZVAL_UNDEF(&attr->args[i].value); |
517 | 72.2k | } |
518 | | |
519 | 1.63M | zend_hash_next_index_insert_ptr(*attributes, attr); |
520 | | |
521 | 1.63M | return attr; |
522 | 1.63M | } |
523 | | |
524 | | static void free_internal_attribute(zval *v) |
525 | 0 | { |
526 | 0 | pefree(Z_PTR_P(v), 1); |
527 | 0 | } |
528 | | |
529 | | ZEND_API zend_internal_attribute *zend_mark_internal_attribute(zend_class_entry *ce) |
530 | 128 | { |
531 | 128 | zend_internal_attribute *internal_attr; |
532 | 128 | zend_attribute *attr; |
533 | | |
534 | 128 | if (ce->type != ZEND_INTERNAL_CLASS) { |
535 | 0 | zend_error_noreturn(E_ERROR, "Only internal classes can be registered as compiler attribute"); |
536 | 0 | } |
537 | | |
538 | 384 | ZEND_HASH_FOREACH_PTR(ce->attributes, attr) { |
539 | 384 | if (zend_string_equals(attr->name, zend_ce_attribute->name)) { |
540 | 128 | internal_attr = pemalloc(sizeof(zend_internal_attribute), 1); |
541 | 128 | internal_attr->ce = ce; |
542 | 128 | internal_attr->flags = Z_LVAL(attr->args[0].value); |
543 | 128 | internal_attr->validator = NULL; |
544 | | |
545 | 128 | zend_string *lcname = zend_string_tolower_ex(ce->name, 1); |
546 | 128 | zend_hash_update_ptr(&internal_attributes, lcname, internal_attr); |
547 | 128 | zend_string_release(lcname); |
548 | | |
549 | 128 | return internal_attr; |
550 | 128 | } |
551 | 384 | } ZEND_HASH_FOREACH_END(); |
552 | | |
553 | 0 | zend_error_noreturn(E_ERROR, "Classes must be first marked as attribute before being able to be registered as internal attribute class"); |
554 | 128 | } |
555 | | |
556 | | ZEND_API zend_internal_attribute *zend_internal_attribute_register(zend_class_entry *ce, uint32_t flags) |
557 | 0 | { |
558 | 0 | zend_attribute *attr = zend_add_class_attribute(ce, zend_ce_attribute->name, 1); |
559 | 0 | ZVAL_LONG(&attr->args[0].value, flags); |
560 | |
|
561 | 0 | return zend_mark_internal_attribute(ce); |
562 | 0 | } |
563 | | |
564 | | ZEND_API zend_internal_attribute *zend_internal_attribute_get(zend_string *lcname) |
565 | 3.26M | { |
566 | 3.26M | return zend_hash_find_ptr(&internal_attributes, lcname); |
567 | 3.26M | } |
568 | | |
569 | | void zend_register_attribute_ce(void) |
570 | 16 | { |
571 | 16 | zend_internal_attribute *attr; |
572 | | |
573 | 16 | zend_hash_init(&internal_attributes, 8, NULL, free_internal_attribute, 1); |
574 | | |
575 | 16 | zend_ce_attribute = register_class_Attribute(); |
576 | 16 | attr = zend_mark_internal_attribute(zend_ce_attribute); |
577 | 16 | attr->validator = validate_attribute; |
578 | | |
579 | 16 | zend_ce_return_type_will_change_attribute = register_class_ReturnTypeWillChange(); |
580 | 16 | zend_mark_internal_attribute(zend_ce_return_type_will_change_attribute); |
581 | | |
582 | 16 | zend_ce_allow_dynamic_properties = register_class_AllowDynamicProperties(); |
583 | 16 | attr = zend_mark_internal_attribute(zend_ce_allow_dynamic_properties); |
584 | 16 | attr->validator = validate_allow_dynamic_properties; |
585 | | |
586 | 16 | zend_ce_sensitive_parameter = register_class_SensitiveParameter(); |
587 | 16 | zend_mark_internal_attribute(zend_ce_sensitive_parameter); |
588 | | |
589 | 16 | memcpy(&attributes_object_handlers_sensitive_parameter_value, &std_object_handlers, sizeof(zend_object_handlers)); |
590 | 16 | attributes_object_handlers_sensitive_parameter_value.get_properties_for = attributes_sensitive_parameter_value_get_properties_for; |
591 | | |
592 | | /* This is not an actual attribute, thus the zend_mark_internal_attribute() call is missing. */ |
593 | 16 | zend_ce_sensitive_parameter_value = register_class_SensitiveParameterValue(); |
594 | 16 | zend_ce_sensitive_parameter_value->default_object_handlers = &attributes_object_handlers_sensitive_parameter_value; |
595 | | |
596 | 16 | zend_ce_override = register_class_Override(); |
597 | 16 | zend_mark_internal_attribute(zend_ce_override); |
598 | | |
599 | 16 | zend_ce_deprecated = register_class_Deprecated(); |
600 | 16 | attr = zend_mark_internal_attribute(zend_ce_deprecated); |
601 | 16 | attr->validator = validate_deprecated; |
602 | | |
603 | 16 | zend_ce_nodiscard = register_class_NoDiscard(); |
604 | 16 | attr = zend_mark_internal_attribute(zend_ce_nodiscard); |
605 | 16 | attr->validator = validate_nodiscard; |
606 | | |
607 | 16 | zend_ce_delayed_target_validation = register_class_DelayedTargetValidation(); |
608 | 16 | attr = zend_mark_internal_attribute(zend_ce_delayed_target_validation); |
609 | 16 | } |
610 | | |
611 | | void zend_attributes_shutdown(void) |
612 | 0 | { |
613 | 0 | zend_hash_destroy(&internal_attributes); |
614 | 0 | } |