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
28ZEND_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
40ZEND_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
74ZEND_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
174ZEND_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
183ZEND_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
266ZEND_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