/src/php-src/Zend/zend_objects_API.c
Line | Count | Source (jump to first uncovered line) |
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: Andi Gutmans <andi@php.net> | |
16 | | | Zeev Suraski <zeev@php.net> | |
17 | | | Dmitry Stogov <dmitry@php.net> | |
18 | | +----------------------------------------------------------------------+ |
19 | | */ |
20 | | |
21 | | #include "zend.h" |
22 | | #include "zend_globals.h" |
23 | | #include "zend_variables.h" |
24 | | #include "zend_API.h" |
25 | | #include "zend_objects_API.h" |
26 | | #include "zend_fibers.h" |
27 | | |
28 | | ZEND_API void ZEND_FASTCALL zend_objects_store_init(zend_objects_store *objects, uint32_t init_size) |
29 | 268k | { |
30 | 268k | objects->object_buckets = (zend_object **) emalloc(init_size * sizeof(zend_object*)); |
31 | 268k | objects->top = 1; /* Skip 0 so that handles are true */ |
32 | 268k | objects->size = init_size; |
33 | 268k | objects->free_list_head = -1; |
34 | 268k | memset(&objects->object_buckets[0], 0, sizeof(zend_object*)); |
35 | 268k | } |
36 | | |
37 | | ZEND_API void ZEND_FASTCALL zend_objects_store_destroy(zend_objects_store *objects) |
38 | 268k | { |
39 | 268k | efree(objects->object_buckets); |
40 | 268k | objects->object_buckets = NULL; |
41 | 268k | } |
42 | | |
43 | | ZEND_API void ZEND_FASTCALL zend_objects_store_call_destructors(zend_objects_store *objects) |
44 | 265k | { |
45 | 265k | EG(flags) |= EG_FLAGS_OBJECT_STORE_NO_REUSE; |
46 | 265k | if (objects->top > 1) { |
47 | 202k | uint32_t i; |
48 | 2.44M | for (i = 1; i < objects->top; i++) { |
49 | 2.23M | zend_object *obj = objects->object_buckets[i]; |
50 | 2.23M | if (IS_OBJ_VALID(obj)) { |
51 | 29.1k | if (!(OBJ_FLAGS(obj) & IS_OBJ_DESTRUCTOR_CALLED)) { |
52 | 24.3k | GC_ADD_FLAGS(obj, IS_OBJ_DESTRUCTOR_CALLED); |
53 | | |
54 | 24.3k | if (obj->handlers->dtor_obj != zend_objects_destroy_object |
55 | 24.3k | || obj->ce->destructor) { |
56 | 2.39k | GC_ADDREF(obj); |
57 | 2.39k | obj->handlers->dtor_obj(obj); |
58 | 2.39k | GC_DELREF(obj); |
59 | 2.39k | } |
60 | 24.3k | } |
61 | 29.1k | } |
62 | 2.23M | } |
63 | 202k | } |
64 | 265k | } |
65 | | |
66 | | ZEND_API void ZEND_FASTCALL zend_objects_store_mark_destructed(zend_objects_store *objects) |
67 | 10.0k | { |
68 | 10.0k | if (objects->object_buckets && objects->top > 1) { |
69 | 3.16k | zend_object **obj_ptr = objects->object_buckets + 1; |
70 | 3.16k | zend_object **end = objects->object_buckets + objects->top; |
71 | | |
72 | 556k | do { |
73 | 556k | zend_object *obj = *obj_ptr; |
74 | | |
75 | 556k | if (IS_OBJ_VALID(obj)) { |
76 | 553k | GC_ADD_FLAGS(obj, IS_OBJ_DESTRUCTOR_CALLED); |
77 | 553k | } |
78 | 556k | obj_ptr++; |
79 | 556k | } while (obj_ptr != end); |
80 | 3.16k | } |
81 | 10.0k | } |
82 | | |
83 | | ZEND_API void ZEND_FASTCALL zend_objects_store_free_object_storage(zend_objects_store *objects, bool fast_shutdown) |
84 | 268k | { |
85 | 268k | zend_object **obj_ptr, **end, *obj; |
86 | | |
87 | 268k | if (objects->top <= 1) { |
88 | 63.2k | return; |
89 | 63.2k | } |
90 | | |
91 | | /* Free object contents, but don't free objects themselves, so they show up as leaks. |
92 | | * Also add a ref to all objects, so the object can't be freed by something else later. */ |
93 | 205k | end = objects->object_buckets + 1; |
94 | 205k | obj_ptr = objects->object_buckets + objects->top; |
95 | | |
96 | 205k | if (fast_shutdown) { |
97 | 0 | do { |
98 | 0 | obj_ptr--; |
99 | 0 | obj = *obj_ptr; |
100 | 0 | if (IS_OBJ_VALID(obj)) { |
101 | 0 | if (!(OBJ_FLAGS(obj) & IS_OBJ_FREE_CALLED)) { |
102 | 0 | GC_ADD_FLAGS(obj, IS_OBJ_FREE_CALLED); |
103 | 0 | if (obj->handlers->free_obj != zend_object_std_dtor |
104 | 0 | || (OBJ_FLAGS(obj) & IS_OBJ_WEAKLY_REFERENCED) |
105 | 0 | ) { |
106 | 0 | GC_ADDREF(obj); |
107 | 0 | obj->handlers->free_obj(obj); |
108 | 0 | } |
109 | 0 | } |
110 | 0 | } |
111 | 0 | } while (obj_ptr != end); |
112 | 205k | } else { |
113 | 2.79M | do { |
114 | 2.79M | obj_ptr--; |
115 | 2.79M | obj = *obj_ptr; |
116 | 2.79M | if (IS_OBJ_VALID(obj)) { |
117 | 566k | if (!(OBJ_FLAGS(obj) & IS_OBJ_FREE_CALLED)) { |
118 | 566k | GC_ADD_FLAGS(obj, IS_OBJ_FREE_CALLED); |
119 | 566k | GC_ADDREF(obj); |
120 | 566k | obj->handlers->free_obj(obj); |
121 | 566k | } |
122 | 566k | } |
123 | 2.79M | } while (obj_ptr != end); |
124 | 205k | } |
125 | 205k | } |
126 | | |
127 | | |
128 | | /* Store objects API */ |
129 | | static ZEND_COLD zend_never_inline void ZEND_FASTCALL zend_objects_store_put_cold(zend_object *object) |
130 | 444 | { |
131 | 444 | int handle; |
132 | 444 | uint32_t new_size = 2 * EG(objects_store).size; |
133 | | |
134 | 444 | EG(objects_store).object_buckets = (zend_object **) erealloc(EG(objects_store).object_buckets, new_size * sizeof(zend_object*)); |
135 | | /* Assign size after realloc, in case it fails */ |
136 | 444 | EG(objects_store).size = new_size; |
137 | 444 | handle = EG(objects_store).top++; |
138 | 444 | object->handle = handle; |
139 | 444 | EG(objects_store).object_buckets[handle] = object; |
140 | 444 | } |
141 | | |
142 | | ZEND_API void ZEND_FASTCALL zend_objects_store_put(zend_object *object) |
143 | 4.07M | { |
144 | 4.07M | int handle; |
145 | | |
146 | | /* When in shutdown sequence - do not reuse previously freed handles, to make sure |
147 | | * the dtors for newly created objects are called in zend_objects_store_call_destructors() loop |
148 | | */ |
149 | 4.07M | if (EG(objects_store).free_list_head != -1 && EXPECTED(!(EG(flags) & EG_FLAGS_OBJECT_STORE_NO_REUSE))) { |
150 | 1.28M | handle = EG(objects_store).free_list_head; |
151 | 1.28M | EG(objects_store).free_list_head = GET_OBJ_BUCKET_NUMBER(EG(objects_store).object_buckets[handle]); |
152 | 2.79M | } else if (UNEXPECTED(EG(objects_store).top == EG(objects_store).size)) { |
153 | 444 | zend_objects_store_put_cold(object); |
154 | 444 | return; |
155 | 2.79M | } else { |
156 | 2.79M | handle = EG(objects_store).top++; |
157 | 2.79M | } |
158 | 4.07M | object->handle = handle; |
159 | 4.07M | EG(objects_store).object_buckets[handle] = object; |
160 | 4.07M | } |
161 | | |
162 | | ZEND_API void ZEND_FASTCALL zend_objects_store_del(zend_object *object) /* {{{ */ |
163 | 3.72M | { |
164 | 3.72M | ZEND_ASSERT(GC_REFCOUNT(object) == 0); |
165 | | |
166 | | /* GC might have released this object already. */ |
167 | 3.72M | if (UNEXPECTED(GC_TYPE(object) == IS_NULL)) { |
168 | 1.65k | return; |
169 | 1.65k | } |
170 | | |
171 | | /* Make sure we hold a reference count during the destructor call |
172 | | otherwise, when the destructor ends the storage might be freed |
173 | | when the refcount reaches 0 a second time |
174 | | */ |
175 | 3.72M | if (!(OBJ_FLAGS(object) & IS_OBJ_DESTRUCTOR_CALLED)) { |
176 | 2.92M | GC_ADD_FLAGS(object, IS_OBJ_DESTRUCTOR_CALLED); |
177 | | |
178 | 2.92M | if (object->handlers->dtor_obj != zend_objects_destroy_object |
179 | 2.92M | || object->ce->destructor) { |
180 | 282k | GC_SET_REFCOUNT(object, 1); |
181 | 282k | object->handlers->dtor_obj(object); |
182 | 282k | GC_DELREF(object); |
183 | 282k | } |
184 | 2.92M | } |
185 | | |
186 | 3.72M | if (GC_REFCOUNT(object) == 0) { |
187 | 3.44M | uint32_t handle = object->handle; |
188 | 3.44M | void *ptr; |
189 | | |
190 | 3.44M | ZEND_ASSERT(EG(objects_store).object_buckets != NULL); |
191 | 3.44M | ZEND_ASSERT(IS_OBJ_VALID(EG(objects_store).object_buckets[handle])); |
192 | 3.44M | EG(objects_store).object_buckets[handle] = SET_OBJ_INVALID(object); |
193 | 3.44M | if (!(OBJ_FLAGS(object) & IS_OBJ_FREE_CALLED)) { |
194 | 3.44M | GC_ADD_FLAGS(object, IS_OBJ_FREE_CALLED); |
195 | 3.44M | GC_SET_REFCOUNT(object, 1); |
196 | 3.44M | object->handlers->free_obj(object); |
197 | 3.44M | } |
198 | 3.44M | ptr = ((char*)object) - object->handlers->offset; |
199 | 3.44M | GC_REMOVE_FROM_BUFFER(object); |
200 | 3.44M | efree(ptr); |
201 | 3.44M | ZEND_OBJECTS_STORE_ADD_TO_FREE_LIST(handle); |
202 | 3.44M | } |
203 | 3.72M | } |
204 | | /* }}} */ |
205 | | |
206 | | ZEND_API ZEND_COLD zend_property_info *zend_get_property_info_for_slot_slow(zend_object *obj, zval *slot) |
207 | 153 | { |
208 | 153 | uintptr_t offset = OBJ_PROP_SLOT_TO_OFFSET(obj, slot); |
209 | 153 | zend_property_info *prop_info; |
210 | 612 | ZEND_HASH_MAP_FOREACH_PTR(&obj->ce->properties_info, prop_info) { |
211 | 612 | if (prop_info->offset == offset) { |
212 | 153 | return prop_info; |
213 | 153 | } |
214 | 612 | } ZEND_HASH_FOREACH_END(); |
215 | 0 | return NULL; |
216 | 153 | } |