コード例 #1
0
ファイル: zend_interfaces.c プロジェクト: dstogov/php-src
/* {{{ zend_implement_aggregate */
static int zend_implement_aggregate(zend_class_entry *interface, zend_class_entry *class_type)
{
	uint32_t i;
	int t = -1;
	zend_class_iterator_funcs *funcs_ptr;

	if (class_type->get_iterator) {
		if (class_type->type == ZEND_INTERNAL_CLASS) {
			/* inheritance ensures the class has necessary userland methods */
			return SUCCESS;
		} else if (class_type->get_iterator != zend_user_it_get_new_iterator) {
			/* c-level get_iterator cannot be changed (exception being only Traversable is implemented) */
			if (class_type->num_interfaces) {
				ZEND_ASSERT(class_type->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_LINKING_IN_PROGRESS));
				for (i = 0; i < class_type->num_interfaces; i++) {
					if (class_type->interfaces[i] == zend_ce_iterator) {
						zend_error_noreturn(E_ERROR, "Class %s cannot implement both %s and %s at the same time",
									ZSTR_VAL(class_type->name),
									ZSTR_VAL(interface->name),
									ZSTR_VAL(zend_ce_iterator->name));
						return FAILURE;
					}
					if (class_type->interfaces[i] == zend_ce_traversable) {
						t = i;
					}
				}
			}
			if (t == -1) {
				return FAILURE;
			}
		}
	}
	if (class_type->parent
	 && (class_type->parent->ce_flags & ZEND_ACC_REUSE_GET_ITERATOR)) {
		class_type->get_iterator = class_type->parent->get_iterator;
		class_type->ce_flags |= ZEND_ACC_REUSE_GET_ITERATOR;
	} else {
		class_type->get_iterator = zend_user_it_get_new_iterator;
	}
	funcs_ptr = class_type->iterator_funcs_ptr;
	if (class_type->type == ZEND_INTERNAL_CLASS) {
		if (!funcs_ptr) {
			funcs_ptr = calloc(1, sizeof(zend_class_iterator_funcs));
			class_type->iterator_funcs_ptr = funcs_ptr;
		}
		funcs_ptr->zf_new_iterator = zend_hash_str_find_ptr(&class_type->function_table, "getiterator", sizeof("getiterator") - 1);
	} else {
		if (!funcs_ptr) {
			funcs_ptr = zend_arena_alloc(&CG(arena), sizeof(zend_class_iterator_funcs));
			class_type->iterator_funcs_ptr = funcs_ptr;
			memset(funcs_ptr, 0, sizeof(zend_class_iterator_funcs));
		} else {
			funcs_ptr->zf_new_iterator = NULL;
		}
	}
	return SUCCESS;
}
コード例 #2
0
ファイル: zend_optimizer.c プロジェクト: BenBuurman/php-src
void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, zval *name, zval* value)
{
	zval val;

	if (!ctx->constants) {
		ctx->constants = zend_arena_alloc(&ctx->arena, sizeof(HashTable));
		zend_hash_init(ctx->constants, 16, NULL, zend_optimizer_zval_dtor_wrapper, 0);
	}
	ZVAL_DUP(&val, value);
	zend_hash_add(ctx->constants, Z_STR_P(name), &val);
}
コード例 #3
0
ファイル: zend_interfaces.c プロジェクト: dstogov/php-src
/* {{{ zend_implement_iterator */
static int zend_implement_iterator(zend_class_entry *interface, zend_class_entry *class_type)
{
	zend_class_iterator_funcs *funcs_ptr;

	if (class_type->get_iterator && class_type->get_iterator != zend_user_it_get_iterator) {
		if (class_type->type == ZEND_INTERNAL_CLASS) {
			/* inheritance ensures the class has the necessary userland methods */
			return SUCCESS;
		} else {
			/* c-level get_iterator cannot be changed */
			if (class_type->get_iterator == zend_user_it_get_new_iterator) {
				zend_error_noreturn(E_ERROR, "Class %s cannot implement both %s and %s at the same time",
							ZSTR_VAL(class_type->name),
							ZSTR_VAL(interface->name),
							ZSTR_VAL(zend_ce_aggregate->name));
			}
			return FAILURE;
		}
	}
	if (class_type->parent
	 && (class_type->parent->ce_flags & ZEND_ACC_REUSE_GET_ITERATOR)) {
		class_type->get_iterator = class_type->parent->get_iterator;
		class_type->ce_flags |= ZEND_ACC_REUSE_GET_ITERATOR;
	} else {
		class_type->get_iterator = zend_user_it_get_iterator;
	}
	funcs_ptr = class_type->iterator_funcs_ptr;
	if (class_type->type == ZEND_INTERNAL_CLASS) {
		if (!funcs_ptr) {
			funcs_ptr = calloc(1, sizeof(zend_class_iterator_funcs));
			class_type->iterator_funcs_ptr = funcs_ptr;
		} else {
			funcs_ptr->zf_rewind = zend_hash_str_find_ptr(&class_type->function_table, "rewind", sizeof("rewind") - 1);
			funcs_ptr->zf_valid = zend_hash_str_find_ptr(&class_type->function_table, "valid", sizeof("valid") - 1);
			funcs_ptr->zf_key = zend_hash_str_find_ptr(&class_type->function_table, "key", sizeof("key") - 1);
			funcs_ptr->zf_current = zend_hash_str_find_ptr(&class_type->function_table, "current", sizeof("current") - 1);
			funcs_ptr->zf_next = zend_hash_str_find_ptr(&class_type->function_table, "next", sizeof("next") - 1);
		}
	} else {
		if (!funcs_ptr) {
			funcs_ptr = zend_arena_alloc(&CG(arena), sizeof(zend_class_iterator_funcs));
			class_type->iterator_funcs_ptr = funcs_ptr;
			memset(funcs_ptr, 0, sizeof(zend_class_iterator_funcs));
		} else {
			funcs_ptr->zf_valid = NULL;
			funcs_ptr->zf_current = NULL;
			funcs_ptr->zf_key = NULL;
			funcs_ptr->zf_next = NULL;
			funcs_ptr->zf_rewind = NULL;
		}
	}
	return SUCCESS;
}
コード例 #4
0
ファイル: php_helpers.c プロジェクト: mushorg/BFR
zend_function *duplicate_function(zend_function *func)
{
	zend_function *new_function;

	if (func->type != ZEND_INTERNAL_FUNCTION)
	{
		new_function = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
		memcpy(new_function, func, sizeof(zend_op_array));

		function_add_ref(new_function);

		return new_function;
	}

	new_function = zend_arena_alloc(&CG(arena), sizeof(zend_internal_function));
	memcpy(new_function, func, sizeof(zend_internal_function));
	new_function->common.fn_flags |= ZEND_ACC_ARENA_ALLOCATED;

	function_add_ref(new_function);

	return new_function;
}
コード例 #5
0
ファイル: phpdbg_opcode.c プロジェクト: AxiosCros/php-src
void phpdbg_print_opline_ex(zend_execute_data *execute_data, zend_bool ignore_flags) /* {{{ */
{
	/* force out a line while stepping so the user knows what is happening */
	if (ignore_flags ||
		(!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) ||
		(PHPDBG_G(flags) & PHPDBG_IS_STEPPING) ||
		(PHPDBG_G(oplog)))) {

		zend_op *opline = (zend_op *) execute_data->opline;
		char *decode = phpdbg_decode_opline(&execute_data->func->op_array, opline);

		if (ignore_flags || (!(PHPDBG_G(flags) & PHPDBG_IS_QUIET) || (PHPDBG_G(flags) & PHPDBG_IS_STEPPING))) {
			/* output line info */
			phpdbg_notice("opline", "line=\"%u\" opline=\"%p\" op=\"%s\" file=\"%s\"", "L%-5u %16p %s %s",
			   opline->lineno,
			   opline,
			   decode,
			   execute_data->func->op_array.filename ? ZSTR_VAL(execute_data->func->op_array.filename) : "unknown");
		}

		if (!ignore_flags && PHPDBG_G(oplog)) {
			phpdbg_log_ex(fileno(PHPDBG_G(oplog)), "L%-5u %16p %s %s\n",
				opline->lineno,
				opline,
				decode,
				execute_data->func->op_array.filename ? ZSTR_VAL(execute_data->func->op_array.filename) : "unknown");
		}

		efree(decode);
	}

	if (PHPDBG_G(oplog_list)) {
		phpdbg_oplog_entry *cur = zend_arena_alloc(&PHPDBG_G(oplog_arena), sizeof(phpdbg_oplog_entry));
		zend_op_array *op_array = &execute_data->func->op_array;
		cur->op = (zend_op *) execute_data->opline;
		cur->opcodes = op_array->opcodes;
		cur->filename = op_array->filename;
		cur->scope = op_array->scope;
		cur->function_name = op_array->function_name;
		cur->next = NULL;
		PHPDBG_G(oplog_cur)->next = cur;
		PHPDBG_G(oplog_cur) = cur;
	}
} /* }}} */
コード例 #6
0
ファイル: zend_ast.c プロジェクト: 13572293130/php-src
static inline void *zend_ast_alloc(size_t size) {
	return zend_arena_alloc(&CG(ast_arena), size);
}
コード例 #7
0
ファイル: zend_file_cache.c プロジェクト: 8liang/php-src
zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handle)
{
	zend_string *full_path = file_handle->opened_path;
	int fd;
	char *filename;
	zend_persistent_script *script;
	zend_file_cache_metainfo info;
	zend_accel_hash_entry *bucket;
	void *mem, *checkpoint, *buf;
	int cache_it = 1;

	if (!full_path) {
		return NULL;
	}
	filename = zend_file_cache_get_bin_file_path(full_path);

	fd = open(filename, O_RDONLY | O_BINARY);
	if (fd < 0) {
		efree(filename);
		return NULL;
	}

	if (zend_file_cache_flock(fd, LOCK_SH) != 0) {
		close(fd);
		efree(filename);
		return NULL;
	}

	if (read(fd, &info, sizeof(info)) != sizeof(info)) {
		zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s'\n", filename);
		zend_file_cache_flock(fd, LOCK_UN);
		close(fd);
		unlink(filename);
		efree(filename);
		return NULL;
	}

	/* verify header */
	if (memcmp(info.magic, "OPCACHE", 8) != 0) {
		zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s' (wrong header)\n", filename);
		zend_file_cache_flock(fd, LOCK_UN);
		close(fd);
		unlink(filename);
		efree(filename);
		return NULL;
	}
	if (memcmp(info.system_id, ZCG(system_id), 32) != 0) {
		zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s' (wrong \"system_id\")\n", filename);
		zend_file_cache_flock(fd, LOCK_UN);
		close(fd);
		unlink(filename);
		efree(filename);
		return NULL;
	}

	/* verify timestamp */
	if (ZCG(accel_directives).validate_timestamps &&
	    zend_get_file_handle_timestamp(file_handle, NULL) != info.timestamp) {
		if (zend_file_cache_flock(fd, LOCK_UN) != 0) {
			zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot unlock file '%s'\n", filename);
		}
		close(fd);
		unlink(filename);
		efree(filename);
		return NULL;
	}

	checkpoint = zend_arena_checkpoint(CG(arena));
#ifdef __SSE2__
	/* Align to 64-byte boundary */
	mem = zend_arena_alloc(&CG(arena), info.mem_size + info.str_size + 64);
	mem = (void*)(((zend_uintptr_t)mem + 63L) & ~63L);
#else
	mem = zend_arena_alloc(&CG(arena), info.mem_size + info.str_size);
#endif

	if (read(fd, mem, info.mem_size + info.str_size) != (ssize_t)(info.mem_size + info.str_size)) {
		zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s'\n", filename);
		zend_file_cache_flock(fd, LOCK_UN);
		close(fd);
		unlink(filename);
		zend_arena_release(&CG(arena), checkpoint);
		efree(filename);
		return NULL;
	}
	if (zend_file_cache_flock(fd, LOCK_UN) != 0) {
		zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot unlock file '%s'\n", filename);
	}
	close(fd);

	/* verify checksum */
	if (ZCG(accel_directives).file_cache_consistency_checks &&
	    zend_adler32(ADLER32_INIT, mem, info.mem_size + info.str_size) != info.checksum) {
		zend_accel_error(ACCEL_LOG_WARNING, "corrupted file '%s'\n", filename);
		unlink(filename);
		zend_arena_release(&CG(arena), checkpoint);
		efree(filename);
		return NULL;
	}

	if (!ZCG(accel_directives).file_cache_only &&
	    !ZCSG(restart_in_progress) &&
	    accelerator_shm_read_lock() == SUCCESS) {
		/* exclusive lock */
		zend_shared_alloc_lock();

		/* Check if we still need to put the file into the cache (may be it was
		 * already stored by another process. This final check is done under
		 * exclusive lock) */
		bucket = zend_accel_hash_find_entry(&ZCSG(hash), full_path);
		if (bucket) {
			script = (zend_persistent_script *)bucket->data;
			if (!script->corrupted) {
				zend_shared_alloc_unlock();
				zend_arena_release(&CG(arena), checkpoint);
				efree(filename);
				return script;
			}
		}

		if (zend_accel_hash_is_full(&ZCSG(hash))) {
			zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
			ZSMMG(memory_exhausted) = 1;
			zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH);
			zend_shared_alloc_unlock();
			goto use_process_mem;
		}

#ifdef __SSE2__
		/* Align to 64-byte boundary */
		buf = zend_shared_alloc(info.mem_size + 64);
		buf = (void*)(((zend_uintptr_t)buf + 63L) & ~63L);
#else
		buf = zend_shared_alloc(info.mem_size);
#endif

		if (!buf) {
			zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
			zend_shared_alloc_unlock();
			goto use_process_mem;
		}
		memcpy(buf, mem, info.mem_size);
	} else {
use_process_mem:
		buf = mem;
		cache_it = 0;
	}

	ZCG(mem) = ((char*)mem + info.mem_size);
	script = (zend_persistent_script*)((char*)buf + info.script_offset);
	script->corrupted = !cache_it; /* used to check if script restored to SHM or process memory */
	zend_file_cache_unserialize(script, buf);
	script->corrupted = 0;

	if (cache_it) {
		script->dynamic_members.checksum = zend_accel_script_checksum(script);
		script->dynamic_members.last_used = ZCG(request_time);

		zend_accel_hash_update(&ZCSG(hash), ZSTR_VAL(script->script.filename), ZSTR_LEN(script->script.filename), 0, script);

		zend_shared_alloc_unlock();
		zend_arena_release(&CG(arena), checkpoint);
	}
	efree(filename);

	return script;
}
コード例 #8
0
ファイル: zend_closures.c プロジェクト: Mingyangzu/php-src
ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) /* {{{ */
{
	zend_closure *closure;

	object_init_ex(res, zend_ce_closure);

	closure = (zend_closure *)Z_OBJ_P(res);

	if ((scope == NULL) && this_ptr && (Z_TYPE_P(this_ptr) != IS_UNDEF)) {
		/* use dummy scope if we're binding an object without specifying a scope */
		/* maybe it would be better to create one for this purpose */
		scope = zend_ce_closure;
	}

	if (func->type == ZEND_USER_FUNCTION) {
		memcpy(&closure->func, func, sizeof(zend_op_array));
		closure->func.common.fn_flags |= ZEND_ACC_CLOSURE;
		if (closure->func.op_array.static_variables) {
			closure->func.op_array.static_variables =
				zend_array_dup(closure->func.op_array.static_variables);
		}

		/* Runtime cache is scope-dependent, so we cannot reuse it if the scope changed */
		if (!closure->func.op_array.run_time_cache
			|| func->common.scope != scope
			|| (func->common.fn_flags & ZEND_ACC_NO_RT_ARENA)
		) {
			if (!func->op_array.run_time_cache && (func->common.fn_flags & ZEND_ACC_CLOSURE)) {
				/* If a real closure is used for the first time, we create a shared runtime cache
				 * and remember which scope it is for. */
				func->common.scope = scope;
				func->op_array.run_time_cache = zend_arena_alloc(&CG(arena), func->op_array.cache_size);
				closure->func.op_array.run_time_cache = func->op_array.run_time_cache;
			} else {
				/* Otherwise, we use a non-shared runtime cache */
				closure->func.op_array.run_time_cache = emalloc(func->op_array.cache_size);
				closure->func.op_array.fn_flags |= ZEND_ACC_NO_RT_ARENA;
			}
			memset(closure->func.op_array.run_time_cache, 0, func->op_array.cache_size);
		}
		if (closure->func.op_array.refcount) {
			(*closure->func.op_array.refcount)++;
		}
	} else {
		memcpy(&closure->func, func, sizeof(zend_internal_function));
		closure->func.common.fn_flags |= ZEND_ACC_CLOSURE;
		/* wrap internal function handler to avoid memory leak */
		if (UNEXPECTED(closure->func.internal_function.handler == zend_closure_internal_handler)) {
			/* avoid infinity recursion, by taking handler from nested closure */
			zend_closure *nested = (zend_closure*)((char*)func - XtOffsetOf(zend_closure, func));
			ZEND_ASSERT(nested->std.ce == zend_ce_closure);
			closure->orig_internal_handler = nested->orig_internal_handler;
		} else {
			closure->orig_internal_handler = closure->func.internal_function.handler;
		}
		closure->func.internal_function.handler = zend_closure_internal_handler;
		if (!func->common.scope) {
			/* if it's a free function, we won't set scope & this since they're meaningless */
			this_ptr = NULL;
			scope = NULL;
		}
	}

	ZVAL_UNDEF(&closure->this_ptr);
	/* Invariant:
	 * If the closure is unscoped or static, it has no bound object. */
	closure->func.common.scope = scope;
	closure->called_scope = called_scope;
	if (scope) {
		closure->func.common.fn_flags |= ZEND_ACC_PUBLIC;
		if (this_ptr && Z_TYPE_P(this_ptr) == IS_OBJECT && (closure->func.common.fn_flags & ZEND_ACC_STATIC) == 0) {
			ZVAL_COPY(&closure->this_ptr, this_ptr);
		}
	}
}
コード例 #9
0
ファイル: optimize_temp_vars_5.c プロジェクト: Tyrael/php-src
void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx)
{
	int T = op_array->T;
	int offset = op_array->last_var;
	char *taken_T;			/* T index in use */
	zend_op **start_of_T;	/* opline where T is first used */
	char *valid_T;			/* Is the map_T valid */
	int *map_T;				/* Map's the T to its new index */
	zend_op *opline, *end;
	int currT;
	int i;
	int max = -1;
	int var_to_free = -1;
	void *checkpoint = zend_arena_checkpoint(ctx->arena);

	taken_T = (char *) zend_arena_alloc(&ctx->arena, T);
	start_of_T = (zend_op **) zend_arena_alloc(&ctx->arena, T * sizeof(zend_op *));
	valid_T = (char *) zend_arena_alloc(&ctx->arena, T);
	map_T = (int *) zend_arena_alloc(&ctx->arena, T * sizeof(int));

    end = op_array->opcodes;
    opline = &op_array->opcodes[op_array->last - 1];

    /* Find T definition points */
    while (opline >= end) {
        if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
			start_of_T[VAR_NUM(ZEND_RESULT(opline).var) - offset] = opline;
		}
		/* special puprose variable to keep HashPointer on VM stack */
		if (opline->opcode == ZEND_OP_DATA &&
		    (opline-1)->opcode == ZEND_FE_FETCH &&
		    opline->op1_type == IS_TMP_VAR) {
			start_of_T[VAR_NUM(ZEND_OP1(opline).var) - offset] = opline;
			if (sizeof(HashPointer) > sizeof(zval)) {
				/* Make shure 1 zval is enough for HashPointer (2 must be enough) */
				start_of_T[VAR_NUM(ZEND_OP1(opline).var) + 1 - offset] = opline;
			}
		}
		opline--;
	}

	memset(valid_T, 0, T);
	memset(taken_T, 0, T);

    end = op_array->opcodes;
    opline = &op_array->opcodes[op_array->last - 1];

    while (opline >= end) {
		if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {

			/* special puprose variable to keep HashPointer on VM stack */
			if (opline->opcode == ZEND_OP_DATA &&
			    (opline-1)->opcode == ZEND_FE_FETCH &&
		    	opline->op1_type == IS_TMP_VAR) {
				max++;
				ZEND_OP1(opline).var = NUM_VAR(max + offset);
				if (sizeof(HashPointer) > sizeof(zval)) {
					/* Make shure 1 zval is enough for HashPointer (2 must be enough) */
					max++;
				}
			} else {
				currT = VAR_NUM(ZEND_OP1(opline).var) - offset;
				if (!valid_T[currT]) {
					GET_AVAILABLE_T();
					map_T[currT] = i;
					valid_T[currT] = 1;
				}
				ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset);
			}
		}

		/* Skip OP_DATA */
		if (opline->opcode == ZEND_OP_DATA &&
		    (opline-1)->opcode == ZEND_ASSIGN_DIM) {
		    opline--;
		    continue;
		}

		if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {
			currT = VAR_NUM(ZEND_OP2(opline).var) - offset;
			if (!valid_T[currT]) {
				GET_AVAILABLE_T();
				map_T[currT] = i;
				valid_T[currT] = 1;
			}
			ZEND_OP2(opline).var = NUM_VAR(map_T[currT] + offset);
		}

		if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS ||
            opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) {
			currT = VAR_NUM(opline->extended_value) - offset;
			if (!valid_T[currT]) {
				GET_AVAILABLE_T();
				map_T[currT] = i;
				valid_T[currT] = 1;
			}
			opline->extended_value = NUM_VAR(map_T[currT] + offset);
		}

		/* Allocate OP_DATA->op2 after "operands", but before "result" */
		if (opline->opcode == ZEND_ASSIGN_DIM &&
		    (opline + 1)->opcode == ZEND_OP_DATA &&
		    ZEND_OP2_TYPE(opline + 1) & (IS_VAR | IS_TMP_VAR)) {
			currT = VAR_NUM(ZEND_OP2(opline + 1).var) - offset;
			GET_AVAILABLE_T();
			map_T[currT] = i;
			valid_T[currT] = 1;
			taken_T[i] = 0;
			ZEND_OP2(opline + 1).var = NUM_VAR(i + offset);
			var_to_free = i;
		}

		if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
			currT = VAR_NUM(ZEND_RESULT(opline).var) - offset;
			if (valid_T[currT]) {
				if (start_of_T[currT] == opline) {
					taken_T[map_T[currT]] = 0;
				}
				ZEND_RESULT(opline).var = NUM_VAR(map_T[currT] + offset);
			} else { /* Au still needs to be assigned a T which is a bit dumb. Should consider changing Zend */
				GET_AVAILABLE_T();

				if (RESULT_UNUSED(opline)) {
					taken_T[i] = 0;
				} else {
					/* Code which gets here is using a wrongly built opcode such as RECV() */
					map_T[currT] = i;
					valid_T[currT] = 1;
				}
				ZEND_RESULT(opline).var = NUM_VAR(i + offset);
			}
		}

		if (var_to_free >= 0) {
			taken_T[var_to_free] = 0;
			var_to_free = -1;
		}

		opline--;
	}

	zend_arena_release(&ctx->arena, checkpoint);
	op_array->T = max + 1;
}
コード例 #10
0
ファイル: zend_closures.c プロジェクト: Jyhwenchai/Share
ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) /* {{{ */
{
	zend_closure *closure;

	object_init_ex(res, zend_ce_closure);

	closure = (zend_closure *)Z_OBJ_P(res);

	if ((scope == NULL) && this_ptr && (Z_TYPE_P(this_ptr) != IS_UNDEF)) {
		/* use dummy scope if we're binding an object without specifying a scope */
		/* maybe it would be better to create one for this purpose */
		scope = zend_ce_closure;
	}

	if (func->type == ZEND_USER_FUNCTION) {
		memcpy(&closure->func, func, sizeof(zend_op_array));
		closure->func.common.prototype = (zend_function*)closure;
		closure->func.common.fn_flags |= ZEND_ACC_CLOSURE;
		if (closure->func.op_array.static_variables) {
			HashTable *static_variables = closure->func.op_array.static_variables;

			ALLOC_HASHTABLE(closure->func.op_array.static_variables);
			zend_hash_init(closure->func.op_array.static_variables, zend_hash_num_elements(static_variables), NULL, ZVAL_PTR_DTOR, 0);
			zend_hash_apply_with_arguments(static_variables, zval_copy_static_var, 1, closure->func.op_array.static_variables);
		}
		if (UNEXPECTED(!closure->func.op_array.run_time_cache)) {
			closure->func.op_array.run_time_cache = func->op_array.run_time_cache = zend_arena_alloc(&CG(arena), func->op_array.cache_size);
			memset(func->op_array.run_time_cache, 0, func->op_array.cache_size);
		}
		if (closure->func.op_array.refcount) {
			(*closure->func.op_array.refcount)++;
		}
	} else {
		memcpy(&closure->func, func, sizeof(zend_internal_function));
		closure->func.common.prototype = (zend_function*)closure;
		closure->func.common.fn_flags |= ZEND_ACC_CLOSURE;
		/* wrap internal function handler to avoid memory leak */
		if (UNEXPECTED(closure->func.internal_function.handler == zend_closure_internal_handler)) {
			/* avoid infinity recursion, by taking handler from nested closure */
			zend_closure *nested = (zend_closure*)((char*)func - XtOffsetOf(zend_closure, func));
			ZEND_ASSERT(nested->std.ce == zend_ce_closure);
			closure->orig_internal_handler = nested->orig_internal_handler;
		} else {
			closure->orig_internal_handler = closure->func.internal_function.handler;
		}
		closure->func.internal_function.handler = zend_closure_internal_handler;
		if (!func->common.scope) {
			/* if it's a free function, we won't set scope & this since they're meaningless */
			this_ptr = NULL;
			scope = NULL;
		}
	}

	ZVAL_UNDEF(&closure->this_ptr);
	/* Invariant:
	 * If the closure is unscoped or static, it has no bound object. */
	closure->func.common.scope = scope;
	closure->called_scope = called_scope;
	if (scope) {
		closure->func.common.fn_flags |= ZEND_ACC_PUBLIC;
		if (this_ptr && Z_TYPE_P(this_ptr) == IS_OBJECT && (closure->func.common.fn_flags & ZEND_ACC_STATIC) == 0) {
			ZVAL_COPY(&closure->this_ptr, this_ptr);
		}
	}
}
コード例 #11
0
void optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx)
{
	int T = op_array->T;
	int offset = op_array->last_var;
	char *taken_T;			/* T index in use */
	zend_op **start_of_T;	/* opline where T is first used */
	char *valid_T;			/* Is the map_T valid */
	int *map_T;				/* Map's the T to its new index */
	zend_op *opline, *end;
	int currT;
	int i;
	int max = -1;
	int var_to_free = -1;
	void *checkpoint = zend_arena_checkpoint(ctx->arena);

	taken_T = (char *) zend_arena_alloc(&ctx->arena, T);
	start_of_T = (zend_op **) zend_arena_alloc(&ctx->arena, T * sizeof(zend_op *));
	valid_T = (char *) zend_arena_alloc(&ctx->arena, T);
	map_T = (int *) zend_arena_alloc(&ctx->arena, T * sizeof(int));

    end = op_array->opcodes;
    opline = &op_array->opcodes[op_array->last - 1];

    /* Find T definition points */
    while (opline >= end) {
        if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
			start_of_T[VAR_NUM(ZEND_RESULT(opline).var) - offset] = opline;
		}
		opline--;
	}

	memset(valid_T, 0, T);
	memset(taken_T, 0, T);

    end = op_array->opcodes;
    opline = &op_array->opcodes[op_array->last - 1];

    while (opline >= end) {
		if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {

			currT = VAR_NUM(ZEND_OP1(opline).var) - offset;
			if (opline->opcode == ZEND_ROPE_END) {
				int num = (((opline->extended_value + 1) * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
				int var;

				var = max;
				while (var >= 0 && !taken_T[var]) {
					var--;
				}
				max = MAX(max, var + num);
				var = var + 1;
				map_T[currT] = var;
				valid_T[currT] = 1;
				taken_T[var] = 1;
				ZEND_OP1(opline).var = NUM_VAR(var + offset);
				while (num > 1) {
					num--;
					taken_T[var + num] = 1;
				}
			} else {
				if (!valid_T[currT]) {
					GET_AVAILABLE_T();
					map_T[currT] = i;
					valid_T[currT] = 1;
				}
				ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset);
			}
		}

		/* Skip OP_DATA */
		if (opline->opcode == ZEND_OP_DATA &&
		    (opline-1)->opcode == ZEND_ASSIGN_DIM) {
		    opline--;
		    continue;
		}

		if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {
			currT = VAR_NUM(ZEND_OP2(opline).var) - offset;
			if (!valid_T[currT]) {
				GET_AVAILABLE_T();
				map_T[currT] = i;
				valid_T[currT] = 1;
			}
			ZEND_OP2(opline).var = NUM_VAR(map_T[currT] + offset);
		}

		if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS ||
		    opline->opcode == ZEND_DECLARE_ANON_INHERITED_CLASS ||
            opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) {
			currT = VAR_NUM(opline->extended_value) - offset;
			if (!valid_T[currT]) {
				GET_AVAILABLE_T();
				map_T[currT] = i;
				valid_T[currT] = 1;
			}
			opline->extended_value = NUM_VAR(map_T[currT] + offset);
		}

		/* Allocate OP_DATA->op2 after "operands", but before "result" */
		if (opline->opcode == ZEND_ASSIGN_DIM &&
		    (opline + 1)->opcode == ZEND_OP_DATA &&
		    ZEND_OP2_TYPE(opline + 1) & (IS_VAR | IS_TMP_VAR)) {
			currT = VAR_NUM(ZEND_OP2(opline + 1).var) - offset;
			GET_AVAILABLE_T();
			map_T[currT] = i;
			valid_T[currT] = 1;
			taken_T[i] = 0;
			ZEND_OP2(opline + 1).var = NUM_VAR(i + offset);
			var_to_free = i;
		}

		if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
			currT = VAR_NUM(ZEND_RESULT(opline).var) - offset;
			if (valid_T[currT]) {
				if (start_of_T[currT] == opline) {
					taken_T[map_T[currT]] = 0;
				}
				ZEND_RESULT(opline).var = NUM_VAR(map_T[currT] + offset);
				if (opline->opcode == ZEND_ROPE_INIT) {
					if (start_of_T[currT] == opline) {
						uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
						while (num > 1) {
							num--;
							taken_T[map_T[currT]+num] = 0;
						}
					}
				}
			} else { /* Au still needs to be assigned a T which is a bit dumb. Should consider changing Zend */
				GET_AVAILABLE_T();

				if (RESULT_UNUSED(opline)) {
					taken_T[i] = 0;
				} else {
					/* Code which gets here is using a wrongly built opcode such as RECV() */
					map_T[currT] = i;
					valid_T[currT] = 1;
				}
				ZEND_RESULT(opline).var = NUM_VAR(i + offset);
			}
		}

		if (var_to_free >= 0) {
			taken_T[var_to_free] = 0;
			var_to_free = -1;
		}

		opline--;
	}

	zend_arena_release(&ctx->arena, checkpoint);
	op_array->T = max + 1;
}
コード例 #12
0
void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx)
{
	zend_op *opline, *end;
	int i, j, n, *map, cache_size;
	zval zv, *pos;
	literal_info *info;
	int l_null = -1;
	int l_false = -1;
	int l_true = -1;
	int l_empty_arr = -1;
	HashTable hash;
	zend_string *key = NULL;
	void *checkpoint = zend_arena_checkpoint(ctx->arena);
	int *const_slot, *class_slot, *func_slot, *bind_var_slot, *property_slot, *method_slot;

	if (op_array->last_literal) {
		info = (literal_info*)zend_arena_calloc(&ctx->arena, op_array->last_literal, sizeof(literal_info));

	    /* Mark literals of specific types */
		opline = op_array->opcodes;
		end = opline + op_array->last;
		while (opline < end) {
			switch (opline->opcode) {
				case ZEND_INIT_FCALL:
					LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 1);
					break;
				case ZEND_INIT_FCALL_BY_NAME:
					LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 2);
					break;
				case ZEND_INIT_NS_FCALL_BY_NAME:
					LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 3);
					break;
				case ZEND_INIT_METHOD_CALL:
					if (opline->op1_type == IS_CONST) {
						LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
					}
					if (opline->op2_type == IS_CONST) {
						LITERAL_INFO(opline->op2.constant, LITERAL_METHOD, 2);
					}
					break;
				case ZEND_INIT_STATIC_METHOD_CALL:
					if (opline->op1_type == IS_CONST) {
						LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
					}
					if (opline->op2_type == IS_CONST) {
						LITERAL_INFO(opline->op2.constant, LITERAL_STATIC_METHOD, 2);
					}
					break;
				case ZEND_CATCH:
					LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
					break;
				case ZEND_DEFINED:
					LITERAL_INFO(opline->op1.constant, LITERAL_CONST, 2);
					break;
				case ZEND_FETCH_CONSTANT:
					if ((opline->op1.num & (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) == (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) {
						LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 5);
					} else {
						LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 3);
					}
					break;
				case ZEND_FETCH_CLASS_CONSTANT:
					if (opline->op1_type == IS_CONST) {
						LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
					}
					LITERAL_INFO(opline->op2.constant, LITERAL_CLASS_CONST, 1);
					break;
				case ZEND_FETCH_STATIC_PROP_R:
				case ZEND_FETCH_STATIC_PROP_W:
				case ZEND_FETCH_STATIC_PROP_RW:
				case ZEND_FETCH_STATIC_PROP_IS:
				case ZEND_FETCH_STATIC_PROP_UNSET:
				case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
				case ZEND_UNSET_STATIC_PROP:
				case ZEND_ISSET_ISEMPTY_STATIC_PROP:
					if (opline->op2_type == IS_CONST) {
						LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 2);
					}
					if (opline->op1_type == IS_CONST) {
						LITERAL_INFO(opline->op1.constant, LITERAL_STATIC_PROPERTY, 1);
					}
					break;
				case ZEND_FETCH_CLASS:
				case ZEND_INSTANCEOF:
					if (opline->op2_type == IS_CONST) {
						LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 2);
					}
					break;
				case ZEND_NEW:
					if (opline->op1_type == IS_CONST) {
						LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
					}
					break;
				case ZEND_ASSIGN_OBJ:
				case ZEND_FETCH_OBJ_R:
				case ZEND_FETCH_OBJ_W:
				case ZEND_FETCH_OBJ_RW:
				case ZEND_FETCH_OBJ_IS:
				case ZEND_FETCH_OBJ_UNSET:
				case ZEND_FETCH_OBJ_FUNC_ARG:
				case ZEND_UNSET_OBJ:
				case ZEND_PRE_INC_OBJ:
				case ZEND_PRE_DEC_OBJ:
				case ZEND_POST_INC_OBJ:
				case ZEND_POST_DEC_OBJ:
				case ZEND_ISSET_ISEMPTY_PROP_OBJ:
					if (opline->op2_type == IS_CONST) {
						LITERAL_INFO(opline->op2.constant, LITERAL_PROPERTY, 1);
					}
					break;
				case ZEND_ASSIGN_ADD:
				case ZEND_ASSIGN_SUB:
				case ZEND_ASSIGN_MUL:
				case ZEND_ASSIGN_DIV:
				case ZEND_ASSIGN_POW:
				case ZEND_ASSIGN_MOD:
				case ZEND_ASSIGN_SL:
				case ZEND_ASSIGN_SR:
				case ZEND_ASSIGN_CONCAT:
				case ZEND_ASSIGN_BW_OR:
				case ZEND_ASSIGN_BW_AND:
				case ZEND_ASSIGN_BW_XOR:
					if (opline->op2_type == IS_CONST) {
						if (opline->extended_value == ZEND_ASSIGN_OBJ) {
							LITERAL_INFO(opline->op2.constant, LITERAL_PROPERTY, 1);
						} else if (opline->extended_value == ZEND_ASSIGN_DIM) {
							if (Z_EXTRA(op_array->literals[opline->op2.constant]) == ZEND_EXTRA_VALUE) {
								LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 2);
							} else {
								LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
							}
						} else {
							LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
						}
					}
					break;
				case ZEND_BIND_GLOBAL:
					LITERAL_INFO(opline->op2.constant, LITERAL_GLOBAL, 1);
					break;
				case ZEND_RECV_INIT:
					LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
					break;
				case ZEND_DECLARE_FUNCTION:
				case ZEND_DECLARE_CLASS:
					LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 2);
					break;
				case ZEND_DECLARE_INHERITED_CLASS:
				case ZEND_DECLARE_INHERITED_CLASS_DELAYED:
					LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 2);
					LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 2);
					break;
				case ZEND_DECLARE_ANON_INHERITED_CLASS:
					LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
					LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 2);
					break;
				case ZEND_ISSET_ISEMPTY_DIM_OBJ:
				case ZEND_ASSIGN_DIM:
				case ZEND_UNSET_DIM:
				case ZEND_FETCH_DIM_R:
				case ZEND_FETCH_DIM_W:
				case ZEND_FETCH_DIM_RW:
				case ZEND_FETCH_DIM_IS:
				case ZEND_FETCH_DIM_FUNC_ARG:
				case ZEND_FETCH_DIM_UNSET:
				case ZEND_FETCH_LIST_R:
				case ZEND_FETCH_LIST_W:
					if (opline->op1_type == IS_CONST) {
						LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
					}
					if (opline->op2_type == IS_CONST) {
						if (Z_EXTRA(op_array->literals[opline->op2.constant]) == ZEND_EXTRA_VALUE) {
							LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 2);
						} else {
							LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
						}
					}
					break;
				default:
					if (opline->op1_type == IS_CONST) {
						LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
					}
					if (opline->op2_type == IS_CONST) {
						LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
					}
					break;
			}
			opline++;
		}

#if DEBUG_COMPACT_LITERALS
		{
			int i, use_copy;
			fprintf(stderr, "File %s func %s\n", op_array->filename->val,
					op_array->function_name ? op_array->function_name->val : "main");
			fprintf(stderr, "Literlas table size %d\n", op_array->last_literal);

			for (i = 0; i < op_array->last_literal; i++) {
				zval zv;
				ZVAL_COPY_VALUE(&zv, op_array->literals + i);
				use_copy = zend_make_printable_zval(op_array->literals + i, &zv);
				fprintf(stderr, "Literal %d, val (%d):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv));
				if (use_copy) {
					zval_ptr_dtor_nogc(&zv);
				}
			}
			fflush(stderr);
		}
#endif

		/* Merge equal constants */
		j = 0;
		zend_hash_init(&hash, op_array->last_literal, NULL, NULL, 0);
		map = (int*)zend_arena_alloc(&ctx->arena, op_array->last_literal * sizeof(int));
		memset(map, 0, op_array->last_literal * sizeof(int));
		for (i = 0; i < op_array->last_literal; i++) {
			if (!info[i].flags) {
				/* unset literal */
				zval_ptr_dtor_nogc(&op_array->literals[i]);
				continue;
			}
			switch (Z_TYPE(op_array->literals[i])) {
				case IS_NULL:
					if (l_null < 0) {
						l_null = j;
						if (i != j) {
							op_array->literals[j] = op_array->literals[i];
							info[j] = info[i];
						}
						j++;
					}
					map[i] = l_null;
					break;
				case IS_FALSE:
					if (l_false < 0) {
						l_false = j;
						if (i != j) {
							op_array->literals[j] = op_array->literals[i];
							info[j] = info[i];
						}
						j++;
					}
					map[i] = l_false;
					break;
				case IS_TRUE:
					if (l_true < 0) {
						l_true = j;
						if (i != j) {
							op_array->literals[j] = op_array->literals[i];
							info[j] = info[i];
						}
						j++;
					}
					map[i] = l_true;
					break;
				case IS_LONG:
					if (LITERAL_NUM_RELATED(info[i].flags) == 1) {
						if ((pos = zend_hash_index_find(&hash, Z_LVAL(op_array->literals[i]))) != NULL) {
							map[i] = Z_LVAL_P(pos);
						} else {
							map[i] = j;
							ZVAL_LONG(&zv, j);
							zend_hash_index_add_new(&hash, Z_LVAL(op_array->literals[i]), &zv);
							if (i != j) {
								op_array->literals[j] = op_array->literals[i];
								info[j] = info[i];
							}
							j++;
						}
					} else {
						ZEND_ASSERT(LITERAL_NUM_RELATED(info[i].flags) == 2);
						key = zend_string_init(Z_STRVAL(op_array->literals[i+1]), Z_STRLEN(op_array->literals[i+1]), 0);
						ZSTR_H(key) = ZSTR_HASH(Z_STR(op_array->literals[i+1])) + 100 +
							LITERAL_NUM_RELATED(info[i].flags) - 1;
						if ((pos = zend_hash_find(&hash, key)) != NULL
						 && LITERAL_NUM_RELATED(info[Z_LVAL_P(pos)].flags) == 2) {
							map[i] = Z_LVAL_P(pos);
							zval_ptr_dtor_nogc(&op_array->literals[i+1]);
						} else {
							map[i] = j;
							ZVAL_LONG(&zv, j);
							zend_hash_add_new(&hash, key, &zv);
							if (i != j) {
								op_array->literals[j] = op_array->literals[i];
								info[j] = info[i];
								op_array->literals[j+1] = op_array->literals[i+1];
								info[j+1] = info[i+1];
							}
							j += 2;
						}
						zend_string_release_ex(key, 0);
						i++;
					}
					break;
				case IS_DOUBLE:
					if ((pos = zend_hash_str_find(&hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double))) != NULL) {
						map[i] = Z_LVAL_P(pos);
					} else {
						map[i] = j;
						ZVAL_LONG(&zv, j);
						zend_hash_str_add(&hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double), &zv);
						if (i != j) {
							op_array->literals[j] = op_array->literals[i];
							info[j] = info[i];
						}
						j++;
					}
					break;
				case IS_STRING:
					if (LITERAL_NUM_RELATED(info[i].flags) == 1) {
						key = zend_string_copy(Z_STR(op_array->literals[i]));
					} else {
						key = zend_string_init(Z_STRVAL(op_array->literals[i]), Z_STRLEN(op_array->literals[i]), 0);
						ZSTR_H(key) = ZSTR_HASH(Z_STR(op_array->literals[i])) +
							LITERAL_NUM_RELATED(info[i].flags) - 1;
					}
					pos = zend_hash_find(&hash, key);
					if (pos != NULL &&
					    Z_TYPE(op_array->literals[Z_LVAL_P(pos)]) == IS_STRING &&
					    LITERAL_NUM_RELATED(info[i].flags) == LITERAL_NUM_RELATED(info[Z_LVAL_P(pos)].flags) &&
					    (LITERAL_NUM_RELATED(info[i].flags) != 2 ||
					     ((info[i].flags & LITERAL_KIND_MASK) != LITERAL_VALUE &&
					      (info[Z_LVAL_P(pos)].flags & LITERAL_KIND_MASK) != LITERAL_VALUE))) {
						zend_string_release_ex(key, 0);
						map[i] = Z_LVAL_P(pos);
						zval_ptr_dtor_nogc(&op_array->literals[i]);
						n = LITERAL_NUM_RELATED(info[i].flags);
						while (n > 1) {
							i++;
							zval_ptr_dtor_nogc(&op_array->literals[i]);
							n--;
						}
					} else {
						map[i] = j;
						ZVAL_LONG(&zv, j);
						zend_hash_add_new(&hash, key, &zv);
						zend_string_release_ex(key, 0);
						if (i != j) {
							op_array->literals[j] = op_array->literals[i];
							info[j] = info[i];
						}
						j++;
						n = LITERAL_NUM_RELATED(info[i].flags);
						while (n > 1) {
							i++;
							if (i != j) op_array->literals[j] = op_array->literals[i];
							j++;
							n--;
						}
					}
					break;
				case IS_ARRAY:
					if (zend_hash_num_elements(Z_ARRVAL(op_array->literals[i])) == 0) {
						if (l_empty_arr < 0) {
							l_empty_arr = j;
							if (i != j) {
								op_array->literals[j] = op_array->literals[i];
								info[j] = info[i];
							}
							j++;
						} else {
							zval_ptr_dtor_nogc(&op_array->literals[i]);
						}
						map[i] = l_empty_arr;
						break;
					}
					/* break missing intentionally */
				default:
					/* don't merge other types */
					map[i] = j;
					if (i != j) {
						op_array->literals[j] = op_array->literals[i];
						info[j] = info[i];
					}
					j++;
					break;
			}
		}
		zend_hash_clean(&hash);
		op_array->last_literal = j;

		const_slot = zend_arena_alloc(&ctx->arena, j * 6 * sizeof(int));
		memset(const_slot, -1, j * 6 * sizeof(int));
		class_slot = const_slot + j;
		func_slot = class_slot + j;
		bind_var_slot = func_slot + j;
		property_slot = bind_var_slot + j;
		method_slot = property_slot + j;

		/* Update opcodes to use new literals table */
		cache_size = 0;
		opline = op_array->opcodes;
		end = opline + op_array->last;
		while (opline < end) {
			if (opline->op1_type == IS_CONST) {
				opline->op1.constant = map[opline->op1.constant];
			}
			if (opline->op2_type == IS_CONST) {
				opline->op2.constant = map[opline->op2.constant];
			}
			switch (opline->opcode) {
				case ZEND_RECV_INIT:
					if (class_name_type_hint(op_array, opline->op1.num)) {
						opline->extended_value = cache_size;
						cache_size += sizeof(void *);
					}
					break;
				case ZEND_RECV:
				case ZEND_RECV_VARIADIC:
					if (class_name_type_hint(op_array, opline->op1.num)) {
						opline->op2.num = cache_size;
						cache_size += sizeof(void *);
					}
					break;
				case ZEND_VERIFY_RETURN_TYPE:
					if (class_name_type_hint(op_array, 0)) {
						opline->op2.num = cache_size;
						cache_size += sizeof(void *);
					}
					break;
				case ZEND_ASSIGN_ADD:
				case ZEND_ASSIGN_SUB:
				case ZEND_ASSIGN_MUL:
				case ZEND_ASSIGN_DIV:
				case ZEND_ASSIGN_POW:
				case ZEND_ASSIGN_MOD:
				case ZEND_ASSIGN_SL:
				case ZEND_ASSIGN_SR:
				case ZEND_ASSIGN_CONCAT:
				case ZEND_ASSIGN_BW_OR:
				case ZEND_ASSIGN_BW_AND:
				case ZEND_ASSIGN_BW_XOR:
					if (opline->extended_value != ZEND_ASSIGN_OBJ) {
						break;
					}
					if (opline->op2_type == IS_CONST) {
						// op2 property
						if (opline->op1_type == IS_UNUSED &&
						    property_slot[opline->op2.constant] >= 0) {
							(opline+1)->extended_value = property_slot[opline->op2.constant];
						} else {
							(opline+1)->extended_value = cache_size;
							cache_size += 2 * sizeof(void *);
							if (opline->op1_type == IS_UNUSED) {
								property_slot[opline->op2.constant] = (opline+1)->extended_value;
							}
						}
					}
					break;
				case ZEND_ASSIGN_OBJ:
				case ZEND_FETCH_OBJ_R:
				case ZEND_FETCH_OBJ_W:
				case ZEND_FETCH_OBJ_RW:
				case ZEND_FETCH_OBJ_IS:
				case ZEND_FETCH_OBJ_UNSET:
				case ZEND_FETCH_OBJ_FUNC_ARG:
				case ZEND_UNSET_OBJ:
				case ZEND_PRE_INC_OBJ:
				case ZEND_PRE_DEC_OBJ:
				case ZEND_POST_INC_OBJ:
				case ZEND_POST_DEC_OBJ:
					if (opline->op2_type == IS_CONST) {
						// op2 property
						if (opline->op1_type == IS_UNUSED &&
						    property_slot[opline->op2.constant] >= 0) {
							opline->extended_value = property_slot[opline->op2.constant];
						} else {
							opline->extended_value = cache_size;
							cache_size += 2 * sizeof(void *);
							if (opline->op1_type == IS_UNUSED) {
								property_slot[opline->op2.constant] = opline->extended_value;
							}
						}
					}
					break;
				case ZEND_ISSET_ISEMPTY_PROP_OBJ:
					if (opline->op2_type == IS_CONST) {
						// op2 property
						if (opline->op1_type == IS_UNUSED &&
						    property_slot[opline->op2.constant] >= 0) {
							opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_ISEMPTY);
						} else {
							opline->extended_value = cache_size | (opline->extended_value & ZEND_ISEMPTY);
							cache_size += 2 * sizeof(void *);
							if (opline->op1_type == IS_UNUSED) {
								property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_ISEMPTY;
							}
						}
					}
					break;
				case ZEND_INIT_FCALL:
				case ZEND_INIT_FCALL_BY_NAME:
				case ZEND_INIT_NS_FCALL_BY_NAME:
					// op2 func
					if (func_slot[opline->op2.constant] >= 0) {
						opline->result.num = func_slot[opline->op2.constant];
					} else {
						opline->result.num = cache_size;
						cache_size += sizeof(void *);
						func_slot[opline->op2.constant] = opline->result.num;
					}
					break;
				case ZEND_INIT_METHOD_CALL:
					if (opline->op2_type == IS_CONST) {
						// op2 method
						if (opline->op1_type == IS_UNUSED &&
						    method_slot[opline->op2.constant] >= 0) {
							opline->result.num = method_slot[opline->op2.constant];
						} else {
							opline->result.num = cache_size;
							cache_size += 2 * sizeof(void *);
							if (opline->op1_type == IS_UNUSED) {
								method_slot[opline->op2.constant] = opline->result.num;
							}
						}
					}
					break;
				case ZEND_INIT_STATIC_METHOD_CALL:
					if (opline->op2_type == IS_CONST) {
						// op2 static method
						if (opline->op1_type == IS_CONST) {
							opline->result.num = add_static_slot(&hash, op_array,
								opline->op1.constant,
								opline->op2.constant,
								LITERAL_STATIC_METHOD,
								&cache_size);
						} else {
							opline->result.num = cache_size;
							cache_size += 2 * sizeof(void *);
						}
					} else if (opline->op1_type == IS_CONST) {
						// op1 class
						if (class_slot[opline->op1.constant] >= 0) {
							opline->result.num = class_slot[opline->op1.constant];
						} else {
							opline->result.num = cache_size;
							cache_size += sizeof(void *);
							class_slot[opline->op1.constant] = opline->result.num;
						}
					}
					break;
				case ZEND_DEFINED:
					// op1 const
					if (const_slot[opline->op1.constant] >= 0) {
						opline->extended_value = const_slot[opline->op1.constant];
					} else {
						opline->extended_value = cache_size;
						cache_size += sizeof(void *);
						const_slot[opline->op1.constant] = opline->extended_value;
					}
					break;
				case ZEND_FETCH_CONSTANT:
					// op2 const
					if (const_slot[opline->op2.constant] >= 0) {
						opline->extended_value = const_slot[opline->op2.constant];
					} else {
						opline->extended_value = cache_size;
						cache_size += sizeof(void *);
						const_slot[opline->op2.constant] = opline->extended_value;
					}
					break;
				case ZEND_FETCH_CLASS_CONSTANT:
					if (opline->op1_type == IS_CONST) {
						// op1/op2 class_const
						opline->extended_value = add_static_slot(&hash, op_array,
							opline->op1.constant,
							opline->op2.constant,
							LITERAL_CLASS_CONST,
							&cache_size);
					} else {
						opline->extended_value = cache_size;
						cache_size += 2 * sizeof(void *);
					}
					break;
				case ZEND_FETCH_STATIC_PROP_R:
				case ZEND_FETCH_STATIC_PROP_W:
				case ZEND_FETCH_STATIC_PROP_RW:
				case ZEND_FETCH_STATIC_PROP_IS:
				case ZEND_FETCH_STATIC_PROP_UNSET:
				case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
				case ZEND_UNSET_STATIC_PROP:
					if (opline->op1_type == IS_CONST) {
						// op1 static property
						if (opline->op2_type == IS_CONST) {
							opline->extended_value = add_static_slot(&hash, op_array,
								opline->op2.constant,
								opline->op1.constant,
								LITERAL_STATIC_PROPERTY,
								&cache_size);
						} else {
							opline->extended_value = cache_size;
							cache_size += 2 * sizeof(void *);
						}
					} else if (opline->op2_type == IS_CONST) {
						// op2 class
						if (class_slot[opline->op2.constant] >= 0) {
							opline->extended_value = class_slot[opline->op2.constant];
						} else {
							opline->extended_value = cache_size;
							cache_size += sizeof(void *);
							class_slot[opline->op2.constant] = opline->extended_value;
						}
					}
					break;
				case ZEND_ISSET_ISEMPTY_STATIC_PROP:
					if (opline->op1_type == IS_CONST) {
						// op1 static property
						if (opline->op2_type == IS_CONST) {
							opline->extended_value = add_static_slot(&hash, op_array,
								opline->op2.constant,
								opline->op1.constant,
								LITERAL_STATIC_PROPERTY,
								&cache_size) | (opline->extended_value & ZEND_ISEMPTY);
						} else {
							opline->extended_value = cache_size | (opline->extended_value & ZEND_ISEMPTY);
							cache_size += 2 * sizeof(void *);
						}
					} else if (opline->op2_type == IS_CONST) {
						// op2 class
						if (class_slot[opline->op2.constant] >= 0) {
							opline->extended_value = class_slot[opline->op2.constant] | (opline->extended_value & ZEND_ISEMPTY);
						} else {
							opline->extended_value = cache_size | (opline->extended_value & ZEND_ISEMPTY);
							cache_size += sizeof(void *);
							class_slot[opline->op2.constant] = opline->extended_value & ~ZEND_ISEMPTY;
						}
					}
					break;
				case ZEND_FETCH_CLASS:
				case ZEND_INSTANCEOF:
					if (opline->op2_type == IS_CONST) {
						// op2 class
						if (class_slot[opline->op2.constant] >= 0) {
							opline->extended_value = class_slot[opline->op2.constant];
						} else {
							opline->extended_value = cache_size;
							cache_size += sizeof(void *);
							class_slot[opline->op2.constant] = opline->extended_value;
						}
					}
					break;
				case ZEND_NEW:
					if (opline->op1_type == IS_CONST) {
						// op1 class
						if (class_slot[opline->op1.constant] >= 0) {
							opline->op2.num = class_slot[opline->op1.constant];
						} else {
							opline->op2.num = cache_size;
							cache_size += sizeof(void *);
							class_slot[opline->op1.constant] = opline->op2.num;
						}
					}
					break;
				case ZEND_CATCH:
					if (opline->op1_type == IS_CONST) {
						// op1 class
						if (class_slot[opline->op1.constant] >= 0) {
							opline->extended_value = class_slot[opline->op1.constant] | (opline->extended_value & ZEND_LAST_CATCH);
						} else {
							opline->extended_value = cache_size | (opline->extended_value & ZEND_LAST_CATCH);
							cache_size += sizeof(void *);
							class_slot[opline->op1.constant] = opline->extended_value & ~ZEND_LAST_CATCH;
						}
					}
					break;
				case ZEND_BIND_GLOBAL:
					// op2 bind var
					if (bind_var_slot[opline->op2.constant] >= 0) {
						opline->extended_value = bind_var_slot[opline->op2.constant];
					} else {
						opline->extended_value = cache_size;
						cache_size += sizeof(void *);
						bind_var_slot[opline->op2.constant] = opline->extended_value;
					}
					break;
			}
			opline++;
		}
		op_array->cache_size = cache_size;
		zend_hash_destroy(&hash);
		zend_arena_release(&ctx->arena, checkpoint);

		if (1) {
			opline = op_array->opcodes;
			while (1) {
				if (opline->opcode == ZEND_RECV_INIT) {
					zval *val = &op_array->literals[opline->op2.constant];

					if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
						uint32_t slot = ZEND_MM_ALIGNED_SIZE_EX(op_array->cache_size, 8);

						Z_CACHE_SLOT_P(val) = slot;
						op_array->cache_size += sizeof(zval);
					}
				} else if (opline->opcode != ZEND_RECV) {
					break;
				}
				opline++;
			}
		}

#if DEBUG_COMPACT_LITERALS
		{
			int i, use_copy;
			fprintf(stderr, "Optimized literlas table size %d\n", op_array->last_literal);

			for (i = 0; i < op_array->last_literal; i++) {
				zval zv;
				ZVAL_COPY_VALUE(&zv, op_array->literals + i);
				use_copy = zend_make_printable_zval(op_array->literals + i, &zv);
				fprintf(stderr, "Literal %d, val (%d):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv));
				if (use_copy) {
					zval_ptr_dtor_nogc(&zv);
				}
			}
			fflush(stderr);
		}
#endif
	}
}
コード例 #13
0
void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx)
{
	int T = op_array->T;
	int offset = op_array->last_var;
	uint32_t bitset_len;
	zend_bitset taken_T;	/* T index in use */
	zend_op **start_of_T;	/* opline where T is first used */
	zend_bitset valid_T;	/* Is the map_T valid */
	int *map_T;				/* Map's the T to its new index */
	zend_op *opline, *end;
	int currT;
	int i;
	int max = -1;
	int var_to_free = -1;
	void *checkpoint = zend_arena_checkpoint(ctx->arena);

	bitset_len = zend_bitset_len(T);
	taken_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
	start_of_T = (zend_op **) zend_arena_alloc(&ctx->arena, T * sizeof(zend_op *));
	valid_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
	map_T = (int *) zend_arena_alloc(&ctx->arena, T * sizeof(int));

    end = op_array->opcodes;
    opline = &op_array->opcodes[op_array->last - 1];

    /* Find T definition points */
    while (opline >= end) {
        if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
			start_of_T[VAR_NUM(ZEND_RESULT(opline).var) - offset] = opline;
		}
		opline--;
	}

	zend_bitset_clear(valid_T, bitset_len);
	zend_bitset_clear(taken_T, bitset_len);

    end = op_array->opcodes;
    opline = &op_array->opcodes[op_array->last - 1];

    while (opline >= end) {
		if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {
			currT = VAR_NUM(ZEND_OP1(opline).var) - offset;
			if (opline->opcode == ZEND_ROPE_END) {
				int num = (((opline->extended_value + 1) * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
				int var;

				var = max;
				while (var >= 0 && !zend_bitset_in(taken_T, var)) {
					var--;
				}
				max = MAX(max, var + num);
				var = var + 1;
				map_T[currT] = var;
				zend_bitset_incl(valid_T, currT);
				zend_bitset_incl(taken_T, var);
				ZEND_OP1(opline).var = NUM_VAR(var + offset);
				while (num > 1) {
					num--;
					zend_bitset_incl(taken_T, var + num);
				}
			} else {
				if (!zend_bitset_in(valid_T, currT)) {
					int use_new_var = 0;

					/* Code in "finally" blocks may modify temorary variables.
					 * We allocate new temporaries for values that need to
					 * relive FAST_CALLs.
					 */
					if ((op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) &&
					    (opline->opcode == ZEND_RETURN ||
					     opline->opcode == ZEND_GENERATOR_RETURN ||
					     opline->opcode == ZEND_RETURN_BY_REF ||
					     opline->opcode == ZEND_FREE ||
					     opline->opcode == ZEND_FE_FREE)) {
						zend_op *curr = opline;

						while (--curr >= end) {
							if (curr->opcode == ZEND_FAST_CALL) {
								use_new_var = 1;
								break;
							} else if (curr->opcode != ZEND_FREE &&
							           curr->opcode != ZEND_FE_FREE &&
							           curr->opcode != ZEND_VERIFY_RETURN_TYPE &&
							           curr->opcode != ZEND_DISCARD_EXCEPTION) {
								break;
							}
						}
					}
					if (use_new_var) {
						i = ++max;
						zend_bitset_incl(taken_T, i);
					} else {
						GET_AVAILABLE_T();
					}
					map_T[currT] = i;
					zend_bitset_incl(valid_T, currT);
				}
				ZEND_OP1(opline).var = NUM_VAR(map_T[currT] + offset);
			}
		}

		if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_TMP_VAR))) {
			currT = VAR_NUM(ZEND_OP2(opline).var) - offset;
			if (!zend_bitset_in(valid_T, currT)) {
				GET_AVAILABLE_T();
				map_T[currT] = i;
				zend_bitset_incl(valid_T, currT);
			}
			ZEND_OP2(opline).var = NUM_VAR(map_T[currT] + offset);
		}

		if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
			currT = VAR_NUM(ZEND_RESULT(opline).var) - offset;
			if (zend_bitset_in(valid_T, currT)) {
				if (start_of_T[currT] == opline) {
					/* ZEND_FAST_CALL can not share temporary var with others
					 * since the fast_var could also be set by ZEND_HANDLE_EXCEPTION
					 * which could be ahead of it */
					if (opline->opcode != ZEND_FAST_CALL) {
						zend_bitset_excl(taken_T, map_T[currT]);
					}
				}
				ZEND_RESULT(opline).var = NUM_VAR(map_T[currT] + offset);
				if (opline->opcode == ZEND_ROPE_INIT) {
					if (start_of_T[currT] == opline) {
						uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
						while (num > 1) {
							num--;
							zend_bitset_excl(taken_T, map_T[currT]+num);
						}
					}
				}
			} else {
				/* Code which gets here is using a wrongly built opcode such as RECV() */
				GET_AVAILABLE_T();
				map_T[currT] = i;
				zend_bitset_incl(valid_T, currT);
				ZEND_RESULT(opline).var = NUM_VAR(i + offset);
			}
		}

		if (var_to_free >= 0) {
			zend_bitset_excl(taken_T, var_to_free);
			var_to_free = -1;
		}

		opline--;
	}

	if (op_array->live_range) {
		for (i = 0; i < op_array->last_live_range; i++) {
			op_array->live_range[i].var =
				NUM_VAR(map_T[VAR_NUM(op_array->live_range[i].var & ~ZEND_LIVE_MASK) - offset] + offset) |
				(op_array->live_range[i].var & ZEND_LIVE_MASK);
		}
	}

	zend_arena_release(&ctx->arena, checkpoint);
	op_array->T = max + 1;
}
コード例 #14
0
zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script, int from_shared_memory)
{
	zend_op_array *op_array;

	op_array = (zend_op_array *) emalloc(sizeof(zend_op_array));
	*op_array = persistent_script->main_op_array;

	if (EXPECTED(from_shared_memory)) {
		zend_hash_init(&ZCG(bind_hash), 10, NULL, NULL, 0);

		ZCG(current_persistent_script) = persistent_script;
		ZCG(arena_mem) = NULL;
		if (EXPECTED(persistent_script->arena_size)) {
#ifdef __SSE2__
			/* Target address must be aligned to 64-byte boundary */
			ZCG(arena_mem) = zend_arena_alloc(&CG(arena), persistent_script->arena_size + 64);
			ZCG(arena_mem) = (void*)(((zend_uintptr_t)ZCG(arena_mem) + 63L) & ~63L);
			fast_memcpy(ZCG(arena_mem), persistent_script->arena_mem, persistent_script->arena_size);
#else
			ZCG(arena_mem) = zend_arena_alloc(&CG(arena), persistent_script->arena_size);
			memcpy(ZCG(arena_mem), persistent_script->arena_mem, persistent_script->arena_size);
#endif
		}

		/* Copy all the necessary stuff from shared memory to regular memory, and protect the shared script */
		if (zend_hash_num_elements(&persistent_script->class_table) > 0) {
			zend_accel_class_hash_copy(CG(class_table), &persistent_script->class_table, (unique_copy_ctor_func_t) zend_class_copy_ctor);
		}
		/* we must first to copy all classes and then prepare functions, since functions may try to bind
		   classes - which depend on pre-bind class entries existent in the class table */
		if (zend_hash_num_elements(&persistent_script->function_table) > 0) {
			zend_accel_function_hash_copy_from_shm(CG(function_table), &persistent_script->function_table);
		}

		/* Register __COMPILER_HALT_OFFSET__ constant */
		if (persistent_script->compiler_halt_offset != 0 &&
		    persistent_script->full_path) {
			zend_string *name;
			char haltoff[] = "__COMPILER_HALT_OFFSET__";

			name = zend_mangle_property_name(haltoff, sizeof(haltoff) - 1, persistent_script->full_path->val, persistent_script->full_path->len, 0);
			if (!zend_hash_exists(EG(zend_constants), name)) {
				zend_register_long_constant(name->val, name->len, persistent_script->compiler_halt_offset, CONST_CS, 0);
			}
			zend_string_release(name);
		}

		zend_hash_destroy(&ZCG(bind_hash));
		ZCG(current_persistent_script) = NULL;
	} else /* if (!from_shared_memory) */ {
		if (zend_hash_num_elements(&persistent_script->function_table) > 0) {
			zend_accel_function_hash_copy(CG(function_table), &persistent_script->function_table);
		}
		if (zend_hash_num_elements(&persistent_script->class_table) > 0) {
			zend_accel_class_hash_copy(CG(class_table), &persistent_script->class_table, NULL);
		}
	}

	if (op_array->early_binding != (uint32_t)-1) {
		zend_string *orig_compiled_filename = CG(compiled_filename);
		CG(compiled_filename) = persistent_script->full_path;
		zend_do_delayed_early_binding(op_array);
		CG(compiled_filename) = orig_compiled_filename;
	}

	if (UNEXPECTED(!from_shared_memory)) {
		free_persistent_script(persistent_script, 0); /* free only hashes */
	}

	return op_array;
}
コード例 #15
0
ファイル: compact_literals.c プロジェクト: LTD-Beget/php-src
void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx)
{
	zend_op *opline, *end;
	int i, j, n, *map, cache_size;
	zval zv, *pos;
	literal_info *info;
	int l_null = -1;
	int l_false = -1;
	int l_true = -1;
	int l_empty_arr = -1;
	HashTable hash;
	zend_string *key = NULL;
	void *checkpoint = zend_arena_checkpoint(ctx->arena);

	if (op_array->last_literal) {
		cache_size = 0;
		info = (literal_info*)zend_arena_calloc(&ctx->arena, op_array->last_literal, sizeof(literal_info));

	    /* Mark literals of specific types */
		opline = op_array->opcodes;
		end = opline + op_array->last;
		while (opline < end) {
			switch (opline->opcode) {
				case ZEND_INIT_FCALL:
					LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 1, 1, 1);
					break;
				case ZEND_INIT_FCALL_BY_NAME:
					LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 1, 1, 2);
					break;
				case ZEND_INIT_NS_FCALL_BY_NAME:
					LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 1, 1, 3);
					break;
				case ZEND_INIT_METHOD_CALL:
					if (ZEND_OP2_TYPE(opline) == IS_CONST) {
						optimizer_literal_obj_info(
							info,
							opline->op1_type,
							opline->op1,
							opline->op2.constant,
							LITERAL_METHOD, 2, 2,
							op_array);
					}
					break;
				case ZEND_INIT_STATIC_METHOD_CALL:
					if (ZEND_OP1_TYPE(opline) == IS_CONST) {
						LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2);
					}
					if (ZEND_OP2_TYPE(opline) == IS_CONST) {
						optimizer_literal_class_info(
							info,
							opline->op1_type,
							opline->op1,
							opline->op2.constant,
							LITERAL_STATIC_METHOD, (ZEND_OP1_TYPE(opline) == IS_CONST) ? 1 : 2, 2,
							op_array);
					}
					break;
				case ZEND_CATCH:
					LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2);
					break;
				case ZEND_DEFINED:
					LITERAL_INFO(opline->op1.constant, LITERAL_CONST, 1, 1, 2);
					break;
				case ZEND_FETCH_CONSTANT:
					if ((opline->extended_value & (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) == (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) {
						LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 1, 1, 5);
					} else {
						LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 1, 1, 3);
					}
					break;
				case ZEND_FETCH_CLASS_CONSTANT:
					if (ZEND_OP1_TYPE(opline) == IS_CONST) {
						LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2);
					}
					optimizer_literal_class_info(
						info,
						opline->op1_type,
						opline->op1,
						opline->op2.constant,
						LITERAL_CLASS_CONST, (ZEND_OP1_TYPE(opline) == IS_CONST) ? 1 : 2, 1,
						op_array);
					break;
				case ZEND_FETCH_STATIC_PROP_R:
				case ZEND_FETCH_STATIC_PROP_W:
				case ZEND_FETCH_STATIC_PROP_RW:
				case ZEND_FETCH_STATIC_PROP_IS:
				case ZEND_FETCH_STATIC_PROP_UNSET:
				case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
				case ZEND_UNSET_STATIC_PROP:
				case ZEND_ISSET_ISEMPTY_STATIC_PROP:
					if (ZEND_OP2_TYPE(opline) == IS_CONST) {
						LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 1, 1, 2);
					}
					if (ZEND_OP1_TYPE(opline) == IS_CONST) {
						optimizer_literal_class_info(
							info,
							opline->op2_type,
							opline->op2,
							opline->op1.constant,
							LITERAL_STATIC_PROPERTY, 2, 1,
							op_array);
					}
					break;
				case ZEND_FETCH_CLASS:
				case ZEND_ADD_INTERFACE:
				case ZEND_ADD_TRAIT:
				case ZEND_INSTANCEOF:
					if (ZEND_OP2_TYPE(opline) == IS_CONST) {
						LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 1, 1, 2);
					}
					break;
				case ZEND_NEW:
					if (ZEND_OP1_TYPE(opline) == IS_CONST) {
						LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2);
					}
					break;
				case ZEND_ASSIGN_OBJ:
				case ZEND_FETCH_OBJ_R:
				case ZEND_FETCH_OBJ_W:
				case ZEND_FETCH_OBJ_RW:
				case ZEND_FETCH_OBJ_IS:
				case ZEND_FETCH_OBJ_UNSET:
				case ZEND_FETCH_OBJ_FUNC_ARG:
				case ZEND_UNSET_OBJ:
				case ZEND_PRE_INC_OBJ:
				case ZEND_PRE_DEC_OBJ:
				case ZEND_POST_INC_OBJ:
				case ZEND_POST_DEC_OBJ:
				case ZEND_ISSET_ISEMPTY_PROP_OBJ:
					if (ZEND_OP2_TYPE(opline) == IS_CONST) {
						optimizer_literal_obj_info(
							info,
							opline->op1_type,
							opline->op1,
							opline->op2.constant,
							LITERAL_PROPERTY, 2, 1,
							op_array);
					}
					break;
				case ZEND_ASSIGN_ADD:
				case ZEND_ASSIGN_SUB:
				case ZEND_ASSIGN_MUL:
				case ZEND_ASSIGN_DIV:
				case ZEND_ASSIGN_POW:
				case ZEND_ASSIGN_MOD:
				case ZEND_ASSIGN_SL:
				case ZEND_ASSIGN_SR:
				case ZEND_ASSIGN_CONCAT:
				case ZEND_ASSIGN_BW_OR:
				case ZEND_ASSIGN_BW_AND:
				case ZEND_ASSIGN_BW_XOR:
					if (ZEND_OP2_TYPE(opline) == IS_CONST) {
						if (opline->extended_value == ZEND_ASSIGN_OBJ) {
							optimizer_literal_obj_info(
								info,
								opline->op1_type,
								opline->op1,
								opline->op2.constant,
								LITERAL_PROPERTY, 2, 1,
								op_array);
						} else {
							LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1, 0, 1);
						}
					}
					break;
				case ZEND_BIND_GLOBAL:
					LITERAL_INFO(opline->op2.constant, LITERAL_GLOBAL, 0, 1, 1);
					break;
				case ZEND_RECV_INIT:
					LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 0, 0, 1);
					if (Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) != (uint32_t)-1) {
						Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) = cache_size;
						cache_size += sizeof(void *);
					}
					break;
				case ZEND_DECLARE_FUNCTION:
				case ZEND_DECLARE_CLASS:
				case ZEND_DECLARE_INHERITED_CLASS:
				case ZEND_DECLARE_INHERITED_CLASS_DELAYED:
					LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 0, 0, 2);
					break;
				case ZEND_RECV:
				case ZEND_RECV_VARIADIC:
				case ZEND_VERIFY_RETURN_TYPE:
					if (opline->op2.num != (uint32_t)-1) {
						opline->op2.num = cache_size;
						cache_size += sizeof(void *);
					}
				default:
					if (ZEND_OP1_TYPE(opline) == IS_CONST) {
						LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1, 0, 1);
					}
					if (ZEND_OP2_TYPE(opline) == IS_CONST) {
						LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1, 0, 1);
					}
					break;
			}
			opline++;
		}

#if DEBUG_COMPACT_LITERALS
		{
			int i, use_copy;
			fprintf(stderr, "File %s func %s\n", op_array->filename->val,
					op_array->function_name ? op_array->function_name->val : "main");
			fprintf(stderr, "Literlas table size %d\n", op_array->last_literal);

			for (i = 0; i < op_array->last_literal; i++) {
				zval zv;
				ZVAL_COPY_VALUE(&zv, op_array->literals + i);
				use_copy = zend_make_printable_zval(op_array->literals + i, &zv);
				fprintf(stderr, "Literal %d, val (%d):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv));
				if (use_copy) {
					zval_dtor(&zv);
				}
			}
			fflush(stderr);
		}
#endif

		/* Merge equal constants */
		j = 0;
		zend_hash_init(&hash, op_array->last_literal, NULL, NULL, 0);
		map = (int*)zend_arena_alloc(&ctx->arena, op_array->last_literal * sizeof(int));
		memset(map, 0, op_array->last_literal * sizeof(int));
		for (i = 0; i < op_array->last_literal; i++) {
			if (!info[i].flags) {
				/* unsed literal */
				zval_dtor(&op_array->literals[i]);
				continue;
			}
			switch (Z_TYPE(op_array->literals[i])) {
				case IS_NULL:
					/* Only checking MAY_MERGE for IS_NULL here 
					 * is because only IS_NULL can be default value for class type hinting(RECV_INIT). */
					if ((info[i].flags & LITERAL_MAY_MERGE)) {
						if (l_null < 0) {
							l_null = j;
							if (i != j) {
								op_array->literals[j] = op_array->literals[i];
								info[j] = info[i];
							}
							j++;
						}
						map[i] = l_null;
					} else {
						map[i] = j;
						if (i != j) {
							op_array->literals[j] = op_array->literals[i];
							info[j] = info[i];
						}
						j++;
					}
					break;
				case IS_FALSE:
					if (l_false < 0) {
						l_false = j;
						if (i != j) {
							op_array->literals[j] = op_array->literals[i];
							info[j] = info[i];
						}
						j++;
					}
					map[i] = l_false;
					break;
				case IS_TRUE:
					if (l_true < 0) {
						l_true = j;
						if (i != j) {
							op_array->literals[j] = op_array->literals[i];
							info[j] = info[i];
						}
						j++;
					}
					map[i] = l_true;
					break;
				case IS_LONG:
					if ((pos = zend_hash_index_find(&hash, Z_LVAL(op_array->literals[i]))) != NULL) {
						map[i] = Z_LVAL_P(pos);
					} else {
						map[i] = j;
						ZVAL_LONG(&zv, j);
						zend_hash_index_add_new(&hash, Z_LVAL(op_array->literals[i]), &zv);
						if (i != j) {
							op_array->literals[j] = op_array->literals[i];
							info[j] = info[i];
						}
						j++;
					}
					break;
				case IS_DOUBLE:
					if ((pos = zend_hash_str_find(&hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double))) != NULL) {
						map[i] = Z_LVAL_P(pos);
					} else {
						map[i] = j;
						ZVAL_LONG(&zv, j);
						zend_hash_str_add(&hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double), &zv);
						if (i != j) {
							op_array->literals[j] = op_array->literals[i];
							info[j] = info[i];
						}
						j++;
					}
					break;
				case IS_STRING:
				case IS_CONSTANT:
					if (info[i].flags & LITERAL_MAY_MERGE) {
						if (info[i].flags & LITERAL_EX_OBJ) {
							int key_len = sizeof("$this->") - 1 + Z_STRLEN(op_array->literals[i]);
							key = zend_string_alloc(key_len, 0);
							memcpy(ZSTR_VAL(key), "$this->", sizeof("$this->") - 1);
							memcpy(ZSTR_VAL(key) + sizeof("$this->") - 1, Z_STRVAL(op_array->literals[i]), Z_STRLEN(op_array->literals[i]) + 1);
							ZSTR_LEN(key) = key_len;
						} else if (info[i].flags & LITERAL_EX_CLASS) {
							int key_len;
							zval *class_name = &op_array->literals[(info[i].u.num < i) ? map[info[i].u.num] : info[i].u.num];
							key_len = Z_STRLEN_P(class_name) + sizeof("::") - 1 + Z_STRLEN(op_array->literals[i]);
							key = zend_string_alloc(key_len, 0);
							memcpy(ZSTR_VAL(key), Z_STRVAL_P(class_name), Z_STRLEN_P(class_name));
							memcpy(ZSTR_VAL(key) + Z_STRLEN_P(class_name), "::", sizeof("::") - 1);
							memcpy(ZSTR_VAL(key) + Z_STRLEN_P(class_name) + sizeof("::") - 1,
								Z_STRVAL(op_array->literals[i]),
								Z_STRLEN(op_array->literals[i]) + 1);
						} else {
							key = zend_string_init(Z_STRVAL(op_array->literals[i]), Z_STRLEN(op_array->literals[i]), 0);
						}
						ZSTR_H(key) = zend_hash_func(ZSTR_VAL(key), ZSTR_LEN(key));
						ZSTR_H(key) += info[i].flags;
					}
					if ((info[i].flags & LITERAL_MAY_MERGE) &&
						(pos = zend_hash_find(&hash, key)) != NULL &&
					   	Z_TYPE(op_array->literals[i]) == Z_TYPE(op_array->literals[Z_LVAL_P(pos)]) &&
						info[i].flags == info[Z_LVAL_P(pos)].flags) {

						zend_string_release(key);
						map[i] = Z_LVAL_P(pos);
						zval_dtor(&op_array->literals[i]);
						n = LITERAL_NUM_RELATED(info[i].flags);
						while (n > 1) {
							i++;
							zval_dtor(&op_array->literals[i]);
							n--;
						}
					} else {
						map[i] = j;
						if (info[i].flags & LITERAL_MAY_MERGE) {
							ZVAL_LONG(&zv, j);
							zend_hash_add_new(&hash, key, &zv);
							zend_string_release(key);
						}
						if (i != j) {
							op_array->literals[j] = op_array->literals[i];
							info[j] = info[i];
						}
						if (LITERAL_NUM_SLOTS(info[i].flags)) {
							Z_CACHE_SLOT(op_array->literals[j]) = cache_size;
							cache_size += LITERAL_NUM_SLOTS(info[i].flags) * sizeof(void*);
						}
						j++;
						n = LITERAL_NUM_RELATED(info[i].flags);
						while (n > 1) {
							i++;
							if (i != j) op_array->literals[j] = op_array->literals[i];
							j++;
							n--;
						}
					}
					break;
				case IS_ARRAY:
					if (zend_hash_num_elements(Z_ARRVAL(op_array->literals[i])) == 0) {
						if (l_empty_arr < 0) {
							l_empty_arr = j;
							if (i != j) {
								op_array->literals[j] = op_array->literals[i];
								info[j] = info[i];
							}
							j++;
						} else {
							zval_dtor(&op_array->literals[i]);
						}
						map[i] = l_empty_arr;
						break;
					}
					/* break missing intentionally */
				default:
					/* don't merge other types */
					map[i] = j;
					if (i != j) {
						op_array->literals[j] = op_array->literals[i];
						info[j] = info[i];
					}
					j++;
					break;
			}
		}
		zend_hash_destroy(&hash);
		op_array->last_literal = j;
		op_array->cache_size = cache_size;

	    /* Update opcodes to use new literals table */
		opline = op_array->opcodes;
		end = opline + op_array->last;
		while (opline < end) {
			if (ZEND_OP1_TYPE(opline) == IS_CONST) {
				opline->op1.constant = map[opline->op1.constant];
			}
			if (ZEND_OP2_TYPE(opline) == IS_CONST) {
				opline->op2.constant = map[opline->op2.constant];
			}
			opline++;
		}
		zend_arena_release(&ctx->arena, checkpoint);

#if DEBUG_COMPACT_LITERALS
		{
			int i, use_copy;
			fprintf(stderr, "Optimized literlas table size %d\n", op_array->last_literal);

			for (i = 0; i < op_array->last_literal; i++) {
				zval zv;
				ZVAL_COPY_VALUE(&zv, op_array->literals + i);
				use_copy = zend_make_printable_zval(op_array->literals + i, &zv);
				fprintf(stderr, "Literal %d, val (%d):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv));
				if (use_copy) {
					zval_dtor(&zv);
				}
			}
			fflush(stderr);
		}
#endif
	}
}
コード例 #16
0
ファイル: zend_closures.c プロジェクト: ralt/php-src
ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) /* {{{ */
{
	zend_closure *closure;

	object_init_ex(res, zend_ce_closure);

	closure = (zend_closure *)Z_OBJ_P(res);

	memcpy(&closure->func, func, func->type == ZEND_USER_FUNCTION ? sizeof(zend_op_array) : sizeof(zend_internal_function));
	closure->func.common.prototype = (zend_function*)closure;
	closure->func.common.fn_flags |= ZEND_ACC_CLOSURE;

	if ((scope == NULL) && this_ptr && (Z_TYPE_P(this_ptr) != IS_UNDEF)) {
		/* use dummy scope if we're binding an object without specifying a scope */
		/* maybe it would be better to create one for this purpose */
		scope = zend_ce_closure;
	}

	if (closure->func.type == ZEND_USER_FUNCTION) {
		if (closure->func.op_array.static_variables) {
			HashTable *static_variables = closure->func.op_array.static_variables;

			ALLOC_HASHTABLE(closure->func.op_array.static_variables);
			zend_hash_init(closure->func.op_array.static_variables, zend_hash_num_elements(static_variables), NULL, ZVAL_PTR_DTOR, 0);
			zend_hash_apply_with_arguments(static_variables, zval_copy_static_var, 1, closure->func.op_array.static_variables);
		}
		if (UNEXPECTED(!closure->func.op_array.run_time_cache)) {
			closure->func.op_array.run_time_cache = func->op_array.run_time_cache = zend_arena_alloc(&CG(arena), func->op_array.cache_size);
			memset(func->op_array.run_time_cache, 0, func->op_array.cache_size);
		}
		if (closure->func.op_array.refcount) {
			(*closure->func.op_array.refcount)++;
		}
	} else {
		/* verify that we aren't binding internal function to a wrong scope */
		if(func->common.scope != NULL) {
			if(scope && !instanceof_function(scope, func->common.scope)) {
				zend_error(E_WARNING, "Cannot bind function %s::%s to scope class %s", ZSTR_VAL(func->common.scope->name), ZSTR_VAL(func->common.function_name), ZSTR_VAL(scope->name));
				scope = NULL;
			}
			if(scope && this_ptr && (func->common.fn_flags & ZEND_ACC_STATIC) == 0 &&
					!instanceof_function(Z_OBJCE_P(this_ptr), closure->func.common.scope)) {
				zend_error(E_WARNING, "Cannot bind function %s::%s to object of class %s", ZSTR_VAL(func->common.scope->name), ZSTR_VAL(func->common.function_name), ZSTR_VAL(Z_OBJCE_P(this_ptr)->name));
				scope = NULL;
				this_ptr = NULL;
			}
		} else {
			/* if it's a free function, we won't set scope & this since they're meaningless */
			this_ptr = NULL;
			scope = NULL;
		}
	}

	ZVAL_UNDEF(&closure->this_ptr);
	/* Invariant:
	 * If the closure is unscoped or static, it has no bound object. */
	closure->func.common.scope = scope;
	closure->called_scope = called_scope;
	if (scope) {
		closure->func.common.fn_flags |= ZEND_ACC_PUBLIC;
		if (this_ptr && Z_TYPE_P(this_ptr) == IS_OBJECT && (closure->func.common.fn_flags & ZEND_ACC_STATIC) == 0) {
			ZVAL_COPY(&closure->this_ptr, this_ptr);
		}
	}
}