1 : /*
2 : +----------------------------------------------------------------------+
3 : | Zend Engine |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1998-2007 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@zend.com> |
16 : | Zeev Suraski <zeev@zend.com> |
17 : +----------------------------------------------------------------------+
18 : */
19 :
20 : /* $Id: zend_objects_API.c,v 1.47.2.6.2.5 2007/01/01 09:35:47 sebastian Exp $ */
21 :
22 : #include "zend.h"
23 : #include "zend_globals.h"
24 : #include "zend_variables.h"
25 : #include "zend_API.h"
26 : #include "zend_objects_API.h"
27 :
28 : #define ZEND_DEBUG_OBJECTS 0
29 :
30 : ZEND_API void zend_objects_store_init(zend_objects_store *objects, zend_uint init_size)
31 219 : {
32 219 : objects->object_buckets = (zend_object_store_bucket *) emalloc(init_size * sizeof(zend_object_store_bucket));
33 219 : objects->top = 1; /* Skip 0 so that handles are true */
34 219 : objects->size = init_size;
35 219 : objects->free_list_head = -1;
36 219 : memset(&objects->object_buckets[0], 0, sizeof(zend_object_store_bucket));
37 219 : }
38 :
39 : ZEND_API void zend_objects_store_destroy(zend_objects_store *objects)
40 219 : {
41 219 : efree(objects->object_buckets);
42 219 : objects->object_buckets = NULL;
43 219 : }
44 :
45 : ZEND_API void zend_objects_store_call_destructors(zend_objects_store *objects TSRMLS_DC)
46 219 : {
47 219 : zend_uint i = 1;
48 :
49 392 : for (i = 1; i < objects->top ; i++) {
50 173 : if (objects->object_buckets[i].valid) {
51 17 : struct _store_object *obj = &objects->object_buckets[i].bucket.obj;
52 :
53 17 : if (!objects->object_buckets[i].destructor_called) {
54 16 : objects->object_buckets[i].destructor_called = 1;
55 16 : if (obj->dtor && obj->object) {
56 16 : obj->refcount++;
57 16 : obj->dtor(obj->object, i TSRMLS_CC);
58 16 : obj->refcount--;
59 : }
60 : }
61 : }
62 : }
63 219 : }
64 :
65 : ZEND_API void zend_objects_store_mark_destructed(zend_objects_store *objects TSRMLS_DC)
66 1 : {
67 : zend_uint i;
68 :
69 1 : if (!objects->object_buckets) {
70 0 : return;
71 : }
72 2 : for (i = 1; i < objects->top ; i++) {
73 1 : if (objects->object_buckets[i].valid) {
74 1 : objects->object_buckets[i].destructor_called = 1;
75 : }
76 : }
77 : }
78 :
79 : ZEND_API void zend_objects_store_free_object_storage(zend_objects_store *objects TSRMLS_DC)
80 219 : {
81 219 : zend_uint i = 1;
82 :
83 392 : for (i = 1; i < objects->top ; i++) {
84 173 : if (objects->object_buckets[i].valid) {
85 11 : struct _store_object *obj = &objects->object_buckets[i].bucket.obj;
86 :
87 11 : objects->object_buckets[i].valid = 0;
88 11 : if (obj->free_storage) {
89 11 : obj->free_storage(obj->object TSRMLS_CC);
90 : }
91 : /* Not adding to free list as we are shutting down anyway */
92 : }
93 : }
94 219 : }
95 :
96 :
97 : /* Store objects API */
98 :
99 : ZEND_API zend_object_handle zend_objects_store_put(void *object, zend_objects_store_dtor_t dtor, zend_objects_free_object_storage_t free_storage, zend_objects_store_clone_t clone TSRMLS_DC)
100 341 : {
101 : zend_object_handle handle;
102 : struct _store_object *obj;
103 :
104 341 : if (EG(objects_store).free_list_head != -1) {
105 168 : handle = EG(objects_store).free_list_head;
106 168 : EG(objects_store).free_list_head = EG(objects_store).object_buckets[handle].bucket.free_list.next;
107 : } else {
108 173 : if (EG(objects_store).top == EG(objects_store).size) {
109 0 : EG(objects_store).size <<= 1;
110 0 : EG(objects_store).object_buckets = (zend_object_store_bucket *) erealloc(EG(objects_store).object_buckets, EG(objects_store).size * sizeof(zend_object_store_bucket));
111 : }
112 173 : handle = EG(objects_store).top++;
113 : }
114 341 : obj = &EG(objects_store).object_buckets[handle].bucket.obj;
115 341 : EG(objects_store).object_buckets[handle].destructor_called = 0;
116 341 : EG(objects_store).object_buckets[handle].valid = 1;
117 :
118 341 : obj->refcount = 1;
119 341 : obj->object = object;
120 341 : obj->dtor = dtor?dtor:(zend_objects_store_dtor_t)zend_objects_destroy_object;
121 341 : obj->free_storage = free_storage;
122 :
123 341 : obj->clone = clone;
124 :
125 : #if ZEND_DEBUG_OBJECTS
126 : fprintf(stderr, "Allocated object id #%d (%s)\n", handle, ((zend_object*)object)->ce->name);
127 : #endif
128 341 : return handle;
129 : }
130 :
131 : ZEND_API zend_uint zend_objects_store_get_refcount(zval *object TSRMLS_DC)
132 0 : {
133 0 : zend_object_handle handle = Z_OBJ_HANDLE_P(object);
134 :
135 0 : return EG(objects_store).object_buckets[handle].bucket.obj.refcount;
136 : }
137 :
138 : ZEND_API void zend_objects_store_add_ref(zval *object TSRMLS_DC)
139 121 : {
140 121 : zend_object_handle handle = Z_OBJ_HANDLE_P(object);
141 :
142 121 : EG(objects_store).object_buckets[handle].bucket.obj.refcount++;
143 : #if ZEND_DEBUG_OBJECTS
144 : fprintf(stderr, "Increased refcount of object id #%d (%s)\n", handle, Z_OBJCE_P(object)->name);
145 : #endif
146 121 : }
147 :
148 : /*
149 : * Add a reference to an objects store entry given the object handle.
150 : */
151 : ZEND_API void zend_objects_store_add_ref_by_handle(zend_object_handle handle TSRMLS_DC)
152 0 : {
153 0 : EG(objects_store).object_buckets[handle].bucket.obj.refcount++;
154 0 : }
155 :
156 : #define ZEND_OBJECTS_STORE_ADD_TO_FREE_LIST() \
157 : EG(objects_store).object_buckets[handle].bucket.free_list.next = EG(objects_store).free_list_head; \
158 : EG(objects_store).free_list_head = handle; \
159 : EG(objects_store).object_buckets[handle].valid = 0;
160 :
161 : ZEND_API void zend_objects_store_del_ref(zval *zobject TSRMLS_DC)
162 457 : {
163 : zend_object_handle handle;
164 :
165 457 : handle = Z_OBJ_HANDLE_P(zobject);
166 :
167 457 : zobject->refcount++;
168 457 : zend_objects_store_del_ref_by_handle(handle TSRMLS_CC);
169 457 : zobject->refcount--;
170 457 : }
171 :
172 : /*
173 : * Delete a reference to an objects store entry given the object handle.
174 : */
175 : ZEND_API void zend_objects_store_del_ref_by_handle(zend_object_handle handle TSRMLS_DC)
176 457 : {
177 : struct _store_object *obj;
178 457 : int failure = 0;
179 :
180 457 : if (!EG(objects_store).object_buckets) {
181 0 : return;
182 : }
183 :
184 457 : obj = &EG(objects_store).object_buckets[handle].bucket.obj;
185 :
186 : /* Make sure we hold a reference count during the destructor call
187 : otherwise, when the destructor ends the storage might be freed
188 : when the refcount reaches 0 a second time
189 : */
190 457 : if (EG(objects_store).object_buckets[handle].valid) {
191 451 : if (obj->refcount == 1) {
192 330 : if (!EG(objects_store).object_buckets[handle].destructor_called) {
193 324 : EG(objects_store).object_buckets[handle].destructor_called = 1;
194 :
195 324 : if (obj->dtor) {
196 324 : zend_try {
197 324 : obj->dtor(obj->object, handle TSRMLS_CC);
198 0 : } zend_catch {
199 0 : failure = 1;
200 324 : } zend_end_try();
201 : }
202 : }
203 330 : if (obj->refcount == 1) {
204 330 : if (obj->free_storage) {
205 326 : zend_try {
206 326 : obj->free_storage(obj->object TSRMLS_CC);
207 0 : } zend_catch {
208 0 : failure = 1;
209 326 : } zend_end_try();
210 : }
211 330 : ZEND_OBJECTS_STORE_ADD_TO_FREE_LIST();
212 : }
213 : }
214 : }
215 :
216 457 : obj->refcount--;
217 :
218 : #if ZEND_DEBUG_OBJECTS
219 : if (obj->refcount == 0) {
220 : fprintf(stderr, "Deallocated object id #%d\n", handle);
221 : } else {
222 : fprintf(stderr, "Decreased refcount of object id #%d\n", handle);
223 : }
224 : #endif
225 457 : if (failure) {
226 0 : zend_bailout();
227 : }
228 : }
229 :
230 : ZEND_API zend_object_value zend_objects_store_clone_obj(zval *zobject TSRMLS_DC)
231 0 : {
232 : zend_object_value retval;
233 : void *new_object;
234 : struct _store_object *obj;
235 0 : zend_object_handle handle = Z_OBJ_HANDLE_P(zobject);
236 :
237 0 : obj = &EG(objects_store).object_buckets[handle].bucket.obj;
238 :
239 0 : if (obj->clone == NULL) {
240 0 : zend_error(E_CORE_ERROR, "Trying to clone uncloneable object of class %s", Z_OBJCE_P(zobject)->name);
241 : }
242 :
243 0 : obj->clone(obj->object, &new_object TSRMLS_CC);
244 :
245 0 : retval.handle = zend_objects_store_put(new_object, obj->dtor, obj->free_storage, obj->clone TSRMLS_CC);
246 0 : retval.handlers = Z_OBJ_HT_P(zobject);
247 :
248 0 : return retval;
249 : }
250 :
251 : ZEND_API void *zend_object_store_get_object(zval *zobject TSRMLS_DC)
252 6241 : {
253 6241 : zend_object_handle handle = Z_OBJ_HANDLE_P(zobject);
254 :
255 6241 : return EG(objects_store).object_buckets[handle].bucket.obj.object;
256 : }
257 :
258 : /*
259 : * Retrieve an entry from the objects store given the object handle.
260 : */
261 : ZEND_API void *zend_object_store_get_object_by_handle(zend_object_handle handle TSRMLS_DC)
262 0 : {
263 0 : return EG(objects_store).object_buckets[handle].bucket.obj.object;
264 : }
265 :
266 : /* zend_object_store_set_object:
267 : * It is ONLY valid to call this function from within the constructor of an
268 : * overloaded object. Its purpose is to set the object pointer for the object
269 : * when you can't possibly know its value until you have parsed the arguments
270 : * from the constructor function. You MUST NOT use this function for any other
271 : * weird games, or call it at any other time after the object is constructed.
272 : * */
273 : ZEND_API void zend_object_store_set_object(zval *zobject, void *object TSRMLS_DC)
274 0 : {
275 0 : zend_object_handle handle = Z_OBJ_HANDLE_P(zobject);
276 :
277 0 : EG(objects_store).object_buckets[handle].bucket.obj.object = object;
278 0 : }
279 :
280 :
281 : /* Called when the ctor was terminated by an exception */
282 : ZEND_API void zend_object_store_ctor_failed(zval *zobject TSRMLS_DC)
283 0 : {
284 0 : zend_object_handle handle = Z_OBJ_HANDLE_P(zobject);
285 :
286 0 : EG(objects_store).object_buckets[handle].destructor_called = 1;
287 0 : }
288 :
289 :
290 : /* Proxy objects workings */
291 : typedef struct _zend_proxy_object {
292 : zval *object;
293 : zval *property;
294 : } zend_proxy_object;
295 :
296 : static zend_object_handlers zend_object_proxy_handlers;
297 :
298 : ZEND_API void zend_objects_proxy_free_storage(zend_proxy_object *object TSRMLS_DC)
299 0 : {
300 0 : zval_ptr_dtor(&object->object);
301 0 : zval_ptr_dtor(&object->property);
302 0 : efree(object);
303 0 : }
304 :
305 : ZEND_API void zend_objects_proxy_clone(zend_proxy_object *object, zend_proxy_object **object_clone TSRMLS_DC)
306 0 : {
307 0 : *object_clone = emalloc(sizeof(zend_proxy_object));
308 0 : (*object_clone)->object = object->object;
309 0 : (*object_clone)->property = object->property;
310 0 : zval_add_ref(&(*object_clone)->property);
311 0 : zval_add_ref(&(*object_clone)->object);
312 0 : }
313 :
314 : ZEND_API zval *zend_object_create_proxy(zval *object, zval *member TSRMLS_DC)
315 0 : {
316 0 : zend_proxy_object *pobj = emalloc(sizeof(zend_proxy_object));
317 : zval *retval;
318 :
319 0 : pobj->object = object;
320 0 : pobj->property = member;
321 0 : zval_add_ref(&pobj->property);
322 0 : zval_add_ref(&pobj->object);
323 :
324 0 : MAKE_STD_ZVAL(retval);
325 0 : Z_TYPE_P(retval) = IS_OBJECT;
326 0 : Z_OBJ_HANDLE_P(retval) = zend_objects_store_put(pobj, NULL, (zend_objects_free_object_storage_t) zend_objects_proxy_free_storage, (zend_objects_store_clone_t) zend_objects_proxy_clone TSRMLS_CC);
327 0 : Z_OBJ_HT_P(retval) = &zend_object_proxy_handlers;
328 :
329 0 : return retval;
330 : }
331 :
332 : ZEND_API void zend_object_proxy_set(zval **property, zval *value TSRMLS_DC)
333 0 : {
334 0 : zend_proxy_object *probj = zend_object_store_get_object(*property TSRMLS_CC);
335 :
336 0 : if (Z_OBJ_HT_P(probj->object) && Z_OBJ_HT_P(probj->object)->write_property) {
337 0 : Z_OBJ_HT_P(probj->object)->write_property(probj->object, probj->property, value TSRMLS_CC);
338 : } else {
339 0 : zend_error(E_WARNING, "Cannot write property of object - no write handler defined");
340 : }
341 0 : }
342 :
343 : ZEND_API zval* zend_object_proxy_get(zval *property TSRMLS_DC)
344 0 : {
345 0 : zend_proxy_object *probj = zend_object_store_get_object(property TSRMLS_CC);
346 :
347 0 : if (Z_OBJ_HT_P(probj->object) && Z_OBJ_HT_P(probj->object)->read_property) {
348 0 : return Z_OBJ_HT_P(probj->object)->read_property(probj->object, probj->property, BP_VAR_R TSRMLS_CC);
349 : } else {
350 0 : zend_error(E_WARNING, "Cannot read property of object - no read handler defined");
351 : }
352 :
353 0 : return NULL;
354 : }
355 :
356 : ZEND_API zend_object_handlers *zend_get_std_object_handlers()
357 3744 : {
358 3744 : return &std_object_handlers;
359 : }
360 :
361 : static zend_object_handlers zend_object_proxy_handlers = {
362 : ZEND_OBJECTS_STORE_HANDLERS,
363 :
364 : NULL, /* read_property */
365 : NULL, /* write_property */
366 : NULL, /* read dimension */
367 : NULL, /* write_dimension */
368 : NULL, /* get_property_ptr_ptr */
369 : zend_object_proxy_get, /* get */
370 : zend_object_proxy_set, /* set */
371 : NULL, /* has_property */
372 : NULL, /* unset_property */
373 : NULL, /* has_dimension */
374 : NULL, /* unset_dimension */
375 : NULL, /* get_properties */
376 : NULL, /* get_method */
377 : NULL, /* call_method */
378 : NULL, /* get_constructor */
379 : NULL, /* get_class_entry */
380 : NULL, /* get_class_name */
381 : NULL, /* compare_objects */
382 : NULL, /* cast_object */
383 : NULL, /* count_elements */
384 : };
385 :
386 :
387 : /*
388 : * Local variables:
389 : * tab-width: 4
390 : * c-basic-offset: 4
391 : * indent-tabs-mode: t
392 : * End:
393 : */
|