1 /*
2 +----------------------------------------------------------------------+
3 | Zend Engine |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1998-2018 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
zend_object_std_init(zend_object * object,zend_class_entry * ce)28 ZEND_API void ZEND_FASTCALL zend_object_std_init(zend_object *object, zend_class_entry *ce)
29 {
30 GC_SET_REFCOUNT(object, 1);
31 GC_TYPE_INFO(object) = IS_OBJECT | (GC_COLLECTABLE << GC_FLAGS_SHIFT);
32 object->ce = ce;
33 object->properties = NULL;
34 zend_objects_store_put(object);
35 if (UNEXPECTED(ce->ce_flags & ZEND_ACC_USE_GUARDS)) {
36 ZVAL_UNDEF(object->properties_table + object->ce->default_properties_count);
37 }
38 }
39
zend_object_std_dtor(zend_object * object)40 ZEND_API void zend_object_std_dtor(zend_object *object)
41 {
42 zval *p, *end;
43
44 if (object->properties) {
45 if (EXPECTED(!(GC_FLAGS(object->properties) & IS_ARRAY_IMMUTABLE))) {
46 if (EXPECTED(GC_DELREF(object->properties) == 0)
47 && EXPECTED(GC_TYPE(object->properties) != IS_NULL)) {
48 zend_array_destroy(object->properties);
49 }
50 }
51 }
52 p = object->properties_table;
53 if (EXPECTED(object->ce->default_properties_count)) {
54 end = p + object->ce->default_properties_count;
55 do {
56 i_zval_ptr_dtor(p ZEND_FILE_LINE_CC);
57 p++;
58 } while (p != end);
59 }
60 if (UNEXPECTED(object->ce->ce_flags & ZEND_ACC_USE_GUARDS)) {
61 if (EXPECTED(Z_TYPE_P(p) == IS_STRING)) {
62 zval_ptr_dtor_str(p);
63 } else if (Z_TYPE_P(p) == IS_ARRAY) {
64 HashTable *guards;
65
66 guards = Z_ARRVAL_P(p);
67 ZEND_ASSERT(guards != NULL);
68 zend_hash_destroy(guards);
69 FREE_HASHTABLE(guards);
70 }
71 }
72 }
73
zend_objects_destroy_object(zend_object * object)74 ZEND_API void zend_objects_destroy_object(zend_object *object)
75 {
76 zend_function *destructor = object->ce->destructor;
77
78 if (destructor) {
79 zend_object *old_exception;
80 zend_class_entry *orig_fake_scope;
81 zend_fcall_info fci;
82 zend_fcall_info_cache fcic;
83 zval ret;
84
85 if (destructor->op_array.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED)) {
86 if (destructor->op_array.fn_flags & ZEND_ACC_PRIVATE) {
87 /* Ensure that if we're calling a private function, we're allowed to do so.
88 */
89 if (EG(current_execute_data)) {
90 zend_class_entry *scope = zend_get_executed_scope();
91
92 if (object->ce != scope) {
93 zend_throw_error(NULL,
94 "Call to private %s::__destruct() from context '%s'",
95 ZSTR_VAL(object->ce->name),
96 scope ? ZSTR_VAL(scope->name) : "");
97 return;
98 }
99 } else {
100 zend_error(E_WARNING,
101 "Call to private %s::__destruct() from context '' during shutdown ignored",
102 ZSTR_VAL(object->ce->name));
103 return;
104 }
105 } else {
106 /* Ensure that if we're calling a protected function, we're allowed to do so.
107 */
108 if (EG(current_execute_data)) {
109 zend_class_entry *scope = zend_get_executed_scope();
110
111 if (!zend_check_protected(zend_get_function_root_class(destructor), scope)) {
112 zend_throw_error(NULL,
113 "Call to protected %s::__destruct() from context '%s'",
114 ZSTR_VAL(object->ce->name),
115 scope ? ZSTR_VAL(scope->name) : "");
116 return;
117 }
118 } else {
119 zend_error(E_WARNING,
120 "Call to protected %s::__destruct() from context '' during shutdown ignored",
121 ZSTR_VAL(object->ce->name));
122 return;
123 }
124 }
125 }
126
127 GC_ADDREF(object);
128
129 /* Make sure that destructors are protected from previously thrown exceptions.
130 * For example, if an exception was thrown in a function and when the function's
131 * local variable destruction results in a destructor being called.
132 */
133 old_exception = NULL;
134 if (EG(exception)) {
135 if (EG(exception) == object) {
136 zend_error_noreturn(E_CORE_ERROR, "Attempt to destruct pending exception");
137 } else {
138 old_exception = EG(exception);
139 EG(exception) = NULL;
140 }
141 }
142 orig_fake_scope = EG(fake_scope);
143 EG(fake_scope) = NULL;
144
145 ZVAL_UNDEF(&ret);
146
147 fci.size = sizeof(fci);
148 fci.object = object;
149 fci.retval = &ret;
150 fci.param_count = 0;
151 fci.params = NULL;
152 fci.no_separation = 1;
153 ZVAL_UNDEF(&fci.function_name); /* Unused */
154
155 fcic.function_handler = destructor;
156 fcic.called_scope = object->ce;
157 fcic.object = object;
158
159 zend_call_function(&fci, &fcic);
160 zval_ptr_dtor(&ret);
161
162 if (old_exception) {
163 if (EG(exception)) {
164 zend_exception_set_previous(EG(exception), old_exception);
165 } else {
166 EG(exception) = old_exception;
167 }
168 }
169 OBJ_RELEASE(object);
170 EG(fake_scope) = orig_fake_scope;
171 }
172 }
173
zend_objects_new(zend_class_entry * ce)174 ZEND_API zend_object* ZEND_FASTCALL zend_objects_new(zend_class_entry *ce)
175 {
176 zend_object *object = emalloc(sizeof(zend_object) + zend_object_properties_size(ce));
177
178 zend_object_std_init(object, ce);
179 object->handlers = &std_object_handlers;
180 return object;
181 }
182
zend_objects_clone_members(zend_object * new_object,zend_object * old_object)183 ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, zend_object *old_object)
184 {
185 if (old_object->ce->default_properties_count) {
186 zval *src = old_object->properties_table;
187 zval *dst = new_object->properties_table;
188 zval *end = src + old_object->ce->default_properties_count;
189
190 do {
191 i_zval_ptr_dtor(dst ZEND_FILE_LINE_CC);
192 ZVAL_COPY_VALUE(dst, src);
193 zval_add_ref(dst);
194 src++;
195 dst++;
196 } while (src != end);
197 } else if (old_object->properties && !old_object->ce->clone) {
198 /* fast copy */
199 if (EXPECTED(old_object->handlers == &std_object_handlers)) {
200 if (EXPECTED(!(GC_FLAGS(old_object->properties) & IS_ARRAY_IMMUTABLE))) {
201 GC_ADDREF(old_object->properties);
202 }
203 new_object->properties = old_object->properties;
204 return;
205 }
206 }
207
208 if (old_object->properties &&
209 EXPECTED(zend_hash_num_elements(old_object->properties))) {
210 zval *prop, new_prop;
211 zend_ulong num_key;
212 zend_string *key;
213
214 if (!new_object->properties) {
215 new_object->properties = zend_new_array(zend_hash_num_elements(old_object->properties));
216 zend_hash_real_init_mixed(new_object->properties);
217 } else {
218 zend_hash_extend(new_object->properties, new_object->properties->nNumUsed + zend_hash_num_elements(old_object->properties), 0);
219 }
220
221 HT_FLAGS(new_object->properties) |=
222 HT_FLAGS(old_object->properties) & HASH_FLAG_HAS_EMPTY_IND;
223
224 ZEND_HASH_FOREACH_KEY_VAL(old_object->properties, num_key, key, prop) {
225 if (Z_TYPE_P(prop) == IS_INDIRECT) {
226 ZVAL_INDIRECT(&new_prop, new_object->properties_table + (Z_INDIRECT_P(prop) - old_object->properties_table));
227 } else {
228 ZVAL_COPY_VALUE(&new_prop, prop);
229 zval_add_ref(&new_prop);
230 }
231 if (EXPECTED(key)) {
232 _zend_hash_append(new_object->properties, key, &new_prop);
233 } else {
234 zend_hash_index_add_new(new_object->properties, num_key, &new_prop);
235 }
236 } ZEND_HASH_FOREACH_END();
237 }
238
239 if (old_object->ce->clone) {
240 zend_fcall_info fci;
241 zend_fcall_info_cache fcic;
242 zval ret;
243
244 GC_ADDREF(new_object);
245
246 ZVAL_UNDEF(&ret);
247
248 fci.size = sizeof(fci);
249 fci.object = new_object;
250 fci.retval = &ret;
251 fci.param_count = 0;
252 fci.params = NULL;
253 fci.no_separation = 1;
254 ZVAL_UNDEF(&fci.function_name); /* Unused */
255
256 fcic.function_handler = new_object->ce->clone;
257 fcic.called_scope = new_object->ce;
258 fcic.object = new_object;
259
260 zend_call_function(&fci, &fcic);
261 zval_ptr_dtor(&ret);
262 OBJ_RELEASE(new_object);
263 }
264 }
265
zend_objects_clone_obj(zval * zobject)266 ZEND_API zend_object *zend_objects_clone_obj(zval *zobject)
267 {
268 zend_object *old_object;
269 zend_object *new_object;
270
271 /* assume that create isn't overwritten, so when clone depends on the
272 * overwritten one then it must itself be overwritten */
273 old_object = Z_OBJ_P(zobject);
274 new_object = zend_objects_new(old_object->ce);
275
276 /* zend_objects_clone_members() expect the properties to be initialized. */
277 if (new_object->ce->default_properties_count) {
278 zval *p = new_object->properties_table;
279 zval *end = p + new_object->ce->default_properties_count;
280 do {
281 ZVAL_UNDEF(p);
282 p++;
283 } while (p != end);
284 }
285
286 zend_objects_clone_members(new_object, old_object);
287
288 return new_object;
289 }
290
291 /*
292 * Local variables:
293 * tab-width: 4
294 * c-basic-offset: 4
295 * indent-tabs-mode: t
296 * End:
297 * vim600: sw=4 ts=4 fdm=marker
298 * vim<600: sw=4 ts=4
299 */
300