Exemplo n.º 1
0
static void ast_create_virtual_node(
		zval *zv, zend_ast_kind kind, zend_ast *ast, zend_long version) {
	zval tmp_zv, tmp_zv2;

	object_init_ex(zv, ast_node_ce);

	ZVAL_LONG(&tmp_zv, kind);
	ast_update_property(zv, AST_STR(kind), &tmp_zv, AST_CACHE_SLOT_KIND);

	ZVAL_LONG(&tmp_zv, ast->attr);
	ast_update_property(zv, AST_STR(flags), &tmp_zv, AST_CACHE_SLOT_FLAGS);

	ZVAL_LONG(&tmp_zv, zend_ast_get_lineno(ast));
	ast_update_property(zv, AST_STR(lineno), &tmp_zv, AST_CACHE_SLOT_LINENO);

	array_init(&tmp_zv);
	ast_update_property(zv, AST_STR(children), &tmp_zv, AST_CACHE_SLOT_CHILDREN);

	ZVAL_COPY(&tmp_zv2, zend_ast_get_zval(ast));
	if (version >= 30) {
		zend_hash_add_new(Z_ARRVAL(tmp_zv), ast_kind_child_name(kind, 0), &tmp_zv2);
	} else {
		zend_hash_next_index_insert(Z_ARRVAL(tmp_zv), &tmp_zv2);
	}
}
Exemplo n.º 2
0
/* This function might be not thread safe at least because it would update the
   hash val in the passed string. Be sure it is called in the appropriate context. */
static zend_always_inline zend_string *zend_add_interned_string(zend_string *str, HashTable *interned_strings, uint32_t flags)
{
	zval val;

	GC_SET_REFCOUNT(str, 1);
	GC_FLAGS(str) |= IS_STR_INTERNED | flags;

	ZVAL_INTERNED_STR(&val, str);

	zend_hash_add_new(interned_strings, str, &val);

	return str;
}
Exemplo n.º 3
0
static void ast_fill_children_ht(HashTable *ht, zend_ast *ast, zend_long version) {
	uint32_t i, count;
	zend_bool is_list = zend_ast_is_list(ast);
	zend_ast **children = ast_get_children(ast, &count);
	for (i = 0; i < count; ++i) {
		zend_ast *child = children[i];
		zend_string *child_name =
			!is_list && version >= 30 ? ast_kind_child_name(ast->kind, i) : NULL;
		zval child_zv;

		if (version >= 20 && ast->kind == ZEND_AST_STMT_LIST
				&& child != NULL && child->kind == ZEND_AST_STMT_LIST) {
			ast_fill_children_ht(ht, child, version);
			continue;
		}

		if (ast_is_name(child, ast, i)) {
			ast_create_virtual_node(&child_zv, AST_NAME, child, version);
		} else if (ast->kind == ZEND_AST_CLOSURE_USES) {
			ast_create_virtual_node(&child_zv, AST_CLOSURE_VAR, child, version);
		} else if (version >= 20 && ast_is_var_name(child, ast, i)) {
			ast_create_virtual_node(&child_zv, ZEND_AST_VAR, child, version);
		} else if (ast->kind == ZEND_AST_PROP_ELEM && i == 2) {
			/* Skip docComment child -- It's handled separately */
			continue;
		} else {
			ast_to_zval(&child_zv, child, version);
		}

		if (child_name) {
			zend_hash_add_new(ht, child_name, &child_zv);
		} else {
			zend_hash_next_index_insert(ht, &child_zv);
		}

	}
}
Exemplo n.º 4
0
static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, int objprops)
{
	while (elements-- > 0) {
		zval key, *data, d, *old_data;
		zend_ulong idx;

		ZVAL_UNDEF(&key);

		if (!php_var_unserialize_ex(&key, p, max, NULL, classes)) {
			zval_dtor(&key);
			return 0;
		}

		data = NULL;
		ZVAL_UNDEF(&d);

		if (!objprops) {
			if (Z_TYPE(key) == IS_LONG) {
				idx = Z_LVAL(key);
numeric_key:
				if (UNEXPECTED((old_data = zend_hash_index_find(ht, idx)) != NULL)) {
					//??? update hash
					var_push_dtor(var_hash, old_data);
					data = zend_hash_index_update(ht, idx, &d);
				} else {
					data = zend_hash_index_add_new(ht, idx, &d);
				}
			} else if (Z_TYPE(key) == IS_STRING) {
				if (UNEXPECTED(ZEND_HANDLE_NUMERIC(Z_STR(key), idx))) {
					goto numeric_key;
				}
				if (UNEXPECTED((old_data = zend_hash_find(ht, Z_STR(key))) != NULL)) {
					//??? update hash
					var_push_dtor(var_hash, old_data);
					data = zend_hash_update(ht, Z_STR(key), &d);
				} else {
					data = zend_hash_add_new(ht, Z_STR(key), &d);
				}
			} else {
				zval_dtor(&key);
				return 0;
			}
		} else {
			if (EXPECTED(Z_TYPE(key) == IS_STRING)) {
string_key:
				if ((old_data = zend_hash_find(ht, Z_STR(key))) != NULL) {
					if (Z_TYPE_P(old_data) == IS_INDIRECT) {
						old_data = Z_INDIRECT_P(old_data);
					}
					var_push_dtor(var_hash, old_data);
					data = zend_hash_update_ind(ht, Z_STR(key), &d);
				} else {
					data = zend_hash_add_new(ht, Z_STR(key), &d);
				}
			} else if (Z_TYPE(key) == IS_LONG) {
				/* object properties should include no integers */
				convert_to_string(&key);
				goto string_key;
			} else {
				zval_dtor(&key);
				return 0;
			}
		}

		if (!php_var_unserialize_ex(data, p, max, var_hash, classes)) {
			zval_dtor(&key);
			return 0;
		}

		if (UNEXPECTED(Z_ISUNDEF_P(data))) {
			if (Z_TYPE(key) == IS_LONG) {
				zend_hash_index_del(ht, Z_LVAL(key));
			} else {
				zend_hash_del_ind(ht, Z_STR(key));
			}
		} else {
			var_push_dtor(var_hash, data);
		}

		zval_dtor(&key);

		if (elements && *(*p-1) != ';' && *(*p-1) != '}') {
			(*p)--;
			return 0;
		}
	}

	return 1;
}
Exemplo n.º 5
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
	}
}
Exemplo n.º 6
0
static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, int objprops)
{
	while (elements-- > 0) {
		zval key, *data, d, *old_data;
		zend_ulong idx;

		ZVAL_UNDEF(&key);

		if (!php_var_unserialize_internal(&key, p, max, NULL)) {
			zval_ptr_dtor(&key);
			return 0;
		}

		data = NULL;
		ZVAL_UNDEF(&d);

		if (!objprops) {
			if (Z_TYPE(key) == IS_LONG) {
				idx = Z_LVAL(key);
numeric_key:
				if (UNEXPECTED((old_data = zend_hash_index_find(ht, idx)) != NULL)) {
					//??? update hash
					var_push_dtor(var_hash, old_data);
					data = zend_hash_index_update(ht, idx, &d);
				} else {
					data = zend_hash_index_add_new(ht, idx, &d);
				}
			} else if (Z_TYPE(key) == IS_STRING) {
				if (UNEXPECTED(ZEND_HANDLE_NUMERIC(Z_STR(key), idx))) {
					goto numeric_key;
				}
				if (UNEXPECTED((old_data = zend_hash_find(ht, Z_STR(key))) != NULL)) {
					//??? update hash
					var_push_dtor(var_hash, old_data);
					data = zend_hash_update(ht, Z_STR(key), &d);
				} else {
					data = zend_hash_add_new(ht, Z_STR(key), &d);
				}
			} else {
				zval_ptr_dtor(&key);
				return 0;
			}
		} else {
			if (EXPECTED(Z_TYPE(key) == IS_STRING)) {
string_key:
				{
					zend_property_info *existing_propinfo;
					zend_string *new_key, *unmangled;
					const char *unmangled_class = NULL; 
					const char *unmangled_prop;
					size_t unmangled_prop_len;

					if (UNEXPECTED(zend_unmangle_property_name_ex(Z_STR(key), &unmangled_class, &unmangled_prop, &unmangled_prop_len) == FAILURE)) {
						zval_ptr_dtor(&key);
						return 0;
					}

					unmangled = zend_string_init(unmangled_prop, unmangled_prop_len, 0);
					if (Z_TYPE_P(rval) == IS_OBJECT
							&& ((existing_propinfo = zend_hash_find_ptr(&Z_OBJCE_P(rval)->properties_info, unmangled)) != NULL) 
							&& (existing_propinfo->flags & ZEND_ACC_PPP_MASK)) {
						if (existing_propinfo->flags & ZEND_ACC_PROTECTED) {
							new_key = zend_mangle_property_name(
								"*", 1, ZSTR_VAL(unmangled), ZSTR_LEN(unmangled), Z_OBJCE_P(rval)->type & ZEND_INTERNAL_CLASS);
							zend_string_release(unmangled);
						} else if (existing_propinfo->flags & ZEND_ACC_PRIVATE) {
							if (unmangled_class != NULL && strcmp(unmangled_class, "*") != 0) {
								new_key = zend_mangle_property_name(
									unmangled_class, strlen(unmangled_class),
									ZSTR_VAL(unmangled), ZSTR_LEN(unmangled),
									Z_OBJCE_P(rval)->type & ZEND_INTERNAL_CLASS);
							} else {
								new_key = zend_mangle_property_name(
									ZSTR_VAL(existing_propinfo->ce->name), ZSTR_LEN(existing_propinfo->ce->name),
									ZSTR_VAL(unmangled), ZSTR_LEN(unmangled),
									Z_OBJCE_P(rval)->type & ZEND_INTERNAL_CLASS);
							}
							zend_string_release(unmangled);
						} else {
							ZEND_ASSERT(existing_propinfo->flags & ZEND_ACC_PUBLIC);
							new_key = unmangled;
						}
						zend_string_release(Z_STR(key));
						ZVAL_STR(&key, new_key);
					} else {
						zend_string_release(unmangled);
					}

					if ((old_data = zend_hash_find(ht, Z_STR(key))) != NULL) {
						if (Z_TYPE_P(old_data) == IS_INDIRECT) {
							old_data = Z_INDIRECT_P(old_data);
						}
						var_push_dtor(var_hash, old_data);
						data = zend_hash_update_ind(ht, Z_STR(key), &d);
					} else {
						data = zend_hash_add_new(ht, Z_STR(key), &d);
					}
				}
			} else if (Z_TYPE(key) == IS_LONG) {
				/* object properties should include no integers */
				convert_to_string(&key);
				goto string_key;
			} else {
				zval_ptr_dtor(&key);
				return 0;
			}
		}

		if (!php_var_unserialize_internal(data, p, max, var_hash)) {
			zval_ptr_dtor(&key);
			return 0;
		}

		var_push_dtor(var_hash, data);
		zval_ptr_dtor(&key);

		if (elements && *(*p-1) != ';' && *(*p-1) != '}') {
			(*p)--;
			return 0;
		}
	}

	return 1;
}
Exemplo n.º 7
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);

	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
	}
}