Coverage Report

Created: 2022-10-14 11:20

/src/php-src/Zend/zend_objects.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_interfaces.h"
26
#include "zend_exceptions.h"
27
#include "zend_weakrefs.h"
28
29
static zend_always_inline void _zend_object_std_init(zend_object *object, zend_class_entry *ce)
30
2.49M
{
31
2.49M
  GC_SET_REFCOUNT(object, 1);
32
2.49M
  GC_TYPE_INFO(object) = GC_OBJECT;
33
2.49M
  object->ce = ce;
34
2.49M
  object->properties = NULL;
35
2.49M
  zend_objects_store_put(object);
36
2.49M
  if (UNEXPECTED(ce->ce_flags & ZEND_ACC_USE_GUARDS)) {
37
0
    ZVAL_UNDEF(object->properties_table + object->ce->default_properties_count);
38
0
  }
39
2.49M
}
40
41
ZEND_API void ZEND_FASTCALL zend_object_std_init(zend_object *object, zend_class_entry *ce)
42
0
{
43
0
  _zend_object_std_init(object, ce);
44
0
}
45
46
ZEND_API void zend_object_std_dtor(zend_object *object)
47
2.49M
{
48
2.49M
  zval *p, *end;
49
50
2.49M
  if (object->properties) {
51
0
    if (EXPECTED(!(GC_FLAGS(object->properties) & IS_ARRAY_IMMUTABLE))) {
52
0
      if (EXPECTED(GC_DELREF(object->properties) == 0)
53
0
          && EXPECTED(GC_TYPE(object->properties) != IS_NULL)) {
54
0
        zend_array_destroy(object->properties);
55
0
      }
56
0
    }
57
0
  }
58
2.49M
  p = object->properties_table;
59
2.49M
  if (EXPECTED(object->ce->default_properties_count)) {
60
2.49M
    end = p + object->ce->default_properties_count;
61
17.4M
    do {
62
17.4M
      if (Z_REFCOUNTED_P(p)) {
63
4.99M
        if (UNEXPECTED(Z_ISREF_P(p)) &&
64
0
            (ZEND_DEBUG || ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(p)))) {
65
0
          zend_property_info *prop_info = zend_get_property_info_for_slot(object, p);
66
0
          if (ZEND_TYPE_IS_SET(prop_info->type)) {
67
0
            ZEND_REF_DEL_TYPE_SOURCE(Z_REF_P(p), prop_info);
68
0
          }
69
0
        }
70
4.99M
        i_zval_ptr_dtor(p);
71
4.99M
      }
72
17.4M
      p++;
73
17.4M
    } while (p != end);
74
2.49M
  }
75
76
2.49M
  if (UNEXPECTED(object->ce->ce_flags & ZEND_ACC_USE_GUARDS)) {
77
0
    if (EXPECTED(Z_TYPE_P(p) == IS_STRING)) {
78
0
      zval_ptr_dtor_str(p);
79
0
    } else if (Z_TYPE_P(p) == IS_ARRAY) {
80
0
      HashTable *guards;
81
82
0
      guards = Z_ARRVAL_P(p);
83
0
      ZEND_ASSERT(guards != NULL);
84
0
      zend_hash_destroy(guards);
85
0
      FREE_HASHTABLE(guards);
86
0
    }
87
0
  }
88
89
2.49M
  if (UNEXPECTED(GC_FLAGS(object) & IS_OBJ_WEAKLY_REFERENCED)) {
90
0
    zend_weakrefs_notify(object);
91
0
  }
92
2.49M
}
93
94
ZEND_API void zend_objects_destroy_object(zend_object *object)
95
0
{
96
0
  zend_function *destructor = object->ce->destructor;
97
98
0
  if (destructor) {
99
0
    zend_object *old_exception;
100
101
0
    if (destructor->op_array.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED)) {
102
0
      if (destructor->op_array.fn_flags & ZEND_ACC_PRIVATE) {
103
        /* Ensure that if we're calling a private function, we're allowed to do so.
104
         */
105
0
        if (EG(current_execute_data)) {
106
0
          zend_class_entry *scope = zend_get_executed_scope();
107
108
0
          if (object->ce != scope) {
109
0
            zend_throw_error(NULL,
110
0
              "Call to private %s::__destruct() from context '%s'",
111
0
              ZSTR_VAL(object->ce->name),
112
0
              scope ? ZSTR_VAL(scope->name) : "");
113
0
            return;
114
0
          }
115
0
        } else {
116
0
          zend_error(E_WARNING,
117
0
            "Call to private %s::__destruct() from context '' during shutdown ignored",
118
0
            ZSTR_VAL(object->ce->name));
119
0
          return;
120
0
        }
121
0
      } else {
122
        /* Ensure that if we're calling a protected function, we're allowed to do so.
123
         */
124
0
        if (EG(current_execute_data)) {
125
0
          zend_class_entry *scope = zend_get_executed_scope();
126
127
0
          if (!zend_check_protected(zend_get_function_root_class(destructor), scope)) {
128
0
            zend_throw_error(NULL,
129
0
              "Call to protected %s::__destruct() from context '%s'",
130
0
              ZSTR_VAL(object->ce->name),
131
0
              scope ? ZSTR_VAL(scope->name) : "");
132
0
            return;
133
0
          }
134
0
        } else {
135
0
          zend_error(E_WARNING,
136
0
            "Call to protected %s::__destruct() from context '' during shutdown ignored",
137
0
            ZSTR_VAL(object->ce->name));
138
0
          return;
139
0
        }
140
0
      }
141
0
    }
142
143
0
    GC_ADDREF(object);
144
145
    /* Make sure that destructors are protected from previously thrown exceptions.
146
     * For example, if an exception was thrown in a function and when the function's
147
     * local variable destruction results in a destructor being called.
148
     */
149
0
    old_exception = NULL;
150
0
    if (EG(exception)) {
151
0
      if (EG(exception) == object) {
152
0
        zend_error_noreturn(E_CORE_ERROR, "Attempt to destruct pending exception");
153
0
      } else {
154
0
        old_exception = EG(exception);
155
0
        EG(exception) = NULL;
156
0
      }
157
0
    }
158
159
0
    zend_call_known_instance_method_with_0_params(destructor, object, NULL);
160
161
0
    if (old_exception) {
162
0
      if (EG(exception)) {
163
0
        zend_exception_set_previous(EG(exception), old_exception);
164
0
      } else {
165
0
        EG(exception) = old_exception;
166
0
      }
167
0
    }
168
0
    OBJ_RELEASE(object);
169
0
  }
170
0
}
171
172
ZEND_API zend_object* ZEND_FASTCALL zend_objects_new(zend_class_entry *ce)
173
2.49M
{
174
2.49M
  zend_object *object = emalloc(sizeof(zend_object) + zend_object_properties_size(ce));
175
176
2.49M
  _zend_object_std_init(object, ce);
177
2.49M
  object->handlers = &std_object_handlers;
178
2.49M
  return object;
179
2.49M
}
180
181
ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, zend_object *old_object)
182
0
{
183
0
  if (old_object->ce->default_properties_count) {
184
0
    zval *src = old_object->properties_table;
185
0
    zval *dst = new_object->properties_table;
186
0
    zval *end = src + old_object->ce->default_properties_count;
187
188
0
    do {
189
0
      i_zval_ptr_dtor(dst);
190
0
      ZVAL_COPY_VALUE_PROP(dst, src);
191
0
      zval_add_ref(dst);
192
0
      if (UNEXPECTED(Z_ISREF_P(dst)) &&
193
0
          (ZEND_DEBUG || ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(dst)))) {
194
0
        zend_property_info *prop_info = zend_get_property_info_for_slot(new_object, dst);
195
0
        if (ZEND_TYPE_IS_SET(prop_info->type)) {
196
0
          ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(dst), prop_info);
197
0
        }
198
0
      }
199
0
      src++;
200
0
      dst++;
201
0
    } while (src != end);
202
0
  } else if (old_object->properties && !old_object->ce->clone) {
203
    /* fast copy */
204
0
    if (EXPECTED(old_object->handlers == &std_object_handlers)) {
205
0
      if (EXPECTED(!(GC_FLAGS(old_object->properties) & IS_ARRAY_IMMUTABLE))) {
206
0
        GC_ADDREF(old_object->properties);
207
0
      }
208
0
      new_object->properties = old_object->properties;
209
0
      return;
210
0
    }
211
0
  }
212
213
0
  if (old_object->properties &&
214
0
      EXPECTED(zend_hash_num_elements(old_object->properties))) {
215
0
    zval *prop, new_prop;
216
0
    zend_ulong num_key;
217
0
    zend_string *key;
218
219
0
    if (!new_object->properties) {
220
0
      new_object->properties = zend_new_array(zend_hash_num_elements(old_object->properties));
221
0
      zend_hash_real_init_mixed(new_object->properties);
222
0
    } else {
223
0
      zend_hash_extend(new_object->properties, new_object->properties->nNumUsed + zend_hash_num_elements(old_object->properties), 0);
224
0
    }
225
226
0
    HT_FLAGS(new_object->properties) |=
227
0
      HT_FLAGS(old_object->properties) & HASH_FLAG_HAS_EMPTY_IND;
228
229
0
    ZEND_HASH_FOREACH_KEY_VAL(old_object->properties, num_key, key, prop) {
230
0
      if (Z_TYPE_P(prop) == IS_INDIRECT) {
231
0
        ZVAL_INDIRECT(&new_prop, new_object->properties_table + (Z_INDIRECT_P(prop) - old_object->properties_table));
232
0
      } else {
233
0
        ZVAL_COPY_VALUE(&new_prop, prop);
234
0
        zval_add_ref(&new_prop);
235
0
      }
236
0
      if (EXPECTED(key)) {
237
0
        _zend_hash_append(new_object->properties, key, &new_prop);
238
0
      } else {
239
0
        zend_hash_index_add_new(new_object->properties, num_key, &new_prop);
240
0
      }
241
0
    } ZEND_HASH_FOREACH_END();
242
0
  }
243
244
0
  if (old_object->ce->clone) {
245
0
    GC_ADDREF(new_object);
246
0
    zend_call_known_instance_method_with_0_params(new_object->ce->clone, new_object, NULL);
247
0
    OBJ_RELEASE(new_object);
248
0
  }
249
0
}
250
251
ZEND_API zend_object *zend_objects_clone_obj(zend_object *old_object)
252
0
{
253
0
  zend_object *new_object;
254
255
  /* assume that create isn't overwritten, so when clone depends on the
256
   * overwritten one then it must itself be overwritten */
257
0
  new_object = zend_objects_new(old_object->ce);
258
259
  /* zend_objects_clone_members() expect the properties to be initialized. */
260
0
  if (new_object->ce->default_properties_count) {
261
0
    zval *p = new_object->properties_table;
262
0
    zval *end = p + new_object->ce->default_properties_count;
263
0
    do {
264
0
      ZVAL_UNDEF(p);
265
0
      p++;
266
0
    } while (p != end);
267
0
  }
268
269
0
  zend_objects_clone_members(new_object, old_object);
270
271
0
  return new_object;
272
0
}