@@ -45,6 +45,8 @@ typedef struct _zval_pair {
45
45
zval value ;
46
46
} zval_pair ;
47
47
48
+ static const zval_pair empty_element_list [0 ];
49
+
48
50
typedef struct _spl_cached_entries {
49
51
size_t size ;
50
52
zval_pair * entries ;
@@ -75,18 +77,22 @@ static spl_cachediterable_object *cached_iterable_from_obj(zend_object *obj)
75
77
*/
76
78
static bool spl_cached_entries_empty (spl_cached_entries * array )
77
79
{
78
- if (array -> entries ) {
79
- ZEND_ASSERT (array -> size > 0 );
80
+ if (array -> size > 0 ) {
81
+ ZEND_ASSERT (array -> entries != empty_element_list );
80
82
return false;
81
83
}
82
- ZEND_ASSERT (array -> size == 0 );
84
+ ZEND_ASSERT (array -> entries == empty_element_list || array -> entries == NULL );
83
85
return true;
84
86
}
85
87
86
- static void spl_cached_entries_default_ctor (spl_cached_entries * array )
88
+ static bool spl_cached_entries_uninitialized (spl_cached_entries * array )
87
89
{
88
- array -> size = 0 ;
89
- array -> entries = NULL ;
90
+ if (array -> entries == NULL ) {
91
+ ZEND_ASSERT (array -> size == 0 );
92
+ return true;
93
+ }
94
+ ZEND_ASSERT ((array -> entries == empty_element_list && array -> size == 0 ) || array -> size > 0 );
95
+ return false;
90
96
}
91
97
92
98
/* Initializes the range [from, to) to null. Does not dtor existing entries. */
@@ -127,7 +133,8 @@ static void spl_cached_entries_init_from_array(spl_cached_entries *array, zend_a
127
133
i ++ ;
128
134
} ZEND_HASH_FOREACH_END ();
129
135
} else {
130
- spl_cached_entries_default_ctor (array );
136
+ array -> size = 0 ;
137
+ array -> entries = (zval_pair * )empty_element_list ;
131
138
}
132
139
}
133
140
@@ -227,8 +234,13 @@ static void spl_cachediterable_copy_range(spl_cached_entries *array, size_t offs
227
234
static void spl_cached_entries_copy_ctor (spl_cached_entries * to , spl_cached_entries * from )
228
235
{
229
236
zend_long size = from -> size ;
237
+ if (!size ) {
238
+ to -> size = 0 ;
239
+ to -> entries = (zval_pair * )empty_element_list ;
240
+ return ;
241
+ }
230
242
231
- to -> size = 0 ; /* reset size in case ecalloc () fails */
243
+ to -> size = 0 ; /* reset size in case emalloc () fails */
232
244
to -> entries = safe_emalloc (size , sizeof (zval_pair ), 0 );
233
245
to -> size = size ;
234
246
@@ -268,6 +280,8 @@ static HashTable* spl_cachediterable_object_get_gc(zend_object *obj, zval **tabl
268
280
* table = & intern -> array .entries [0 ].key ;
269
281
* n = (int )intern -> array .size * 2 ;
270
282
283
+ // TODO: This is probably redundant if dynamic properties are not allowed
284
+ // and the properties can't be mutated.
271
285
return ht ;
272
286
}
273
287
@@ -313,6 +327,8 @@ static zend_object *spl_cachediterable_object_new_ex(zend_class_entry *class_typ
313
327
if (orig && clone_orig ) {
314
328
spl_cachediterable_object * other = cached_iterable_from_obj (orig );
315
329
spl_cached_entries_copy_ctor (& intern -> array , & other -> array );
330
+ } else {
331
+ intern -> array .entries = NULL ;
316
332
}
317
333
318
334
return & intern -> std ;
@@ -365,9 +381,10 @@ PHP_METHOD(CachedIterable, __construct)
365
381
366
382
spl_cachediterable_object * intern = Z_CACHEDITERABLE_P (object );
367
383
368
- if (!spl_cached_entries_empty (& intern -> array )) {
384
+ if (UNEXPECTED (!spl_cached_entries_uninitialized (& intern -> array ))) {
385
+ zend_throw_exception (spl_ce_RuntimeException , "Called CachedIterable::__construct twice" , 0 );
369
386
/* called __construct() twice, bail out */
370
- return ;
387
+ RETURN_THROWS () ;
371
388
}
372
389
373
390
switch (Z_TYPE_P (iterable )) {
@@ -503,10 +520,11 @@ PHP_METHOD(CachedIterable, __unserialize)
503
520
RETURN_THROWS ();
504
521
}
505
522
spl_cachediterable_object * intern = Z_CACHEDITERABLE_P (ZEND_THIS );
506
- if (intern -> array . size ) {
523
+ if (UNEXPECTED (! spl_cached_entries_uninitialized ( & intern -> array )) ) {
507
524
zend_throw_exception (spl_ce_RuntimeException , "Already unserialized" , 0 );
508
525
RETURN_THROWS ();
509
526
}
527
+
510
528
ZEND_ASSERT (intern -> array .entries == NULL );
511
529
512
530
size_t num_entries = raw_size / 2 ;
0 commit comments