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