ZEND_VM_HANDLER(24, ZEND_ASSIGN_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, CACHE_SLOT, SPEC(OP_DATA=CONST|TMP|VAR|CV))
{
	USE_OPLINE
	zend_free_op free_op1, free_op2, free_op_data;
	zval *object, *property, *value, tmp;

	SAVE_OPLINE();
	object = GET_OP1_OBJ_ZVAL_PTR_PTR_UNDEF(BP_VAR_W);

	if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
		ZEND_VM_DISPATCH_TO_HELPER(zend_this_not_in_object_context_helper);
	}

	property = GET_OP2_ZVAL_PTR(BP_VAR_R);
	value = GET_OP_DATA_ZVAL_PTR(BP_VAR_R);

	if (OP1_TYPE != IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) {
		if (Z_ISREF_P(object) && Z_TYPE_P(Z_REFVAL_P(object)) == IS_OBJECT) {
			object = Z_REFVAL_P(object);
			ZEND_VM_C_GOTO(assign_object);
		}
		object = make_real_object(object, property OPLINE_CC EXECUTE_DATA_CC);
		if (UNEXPECTED(!object)) {
			value = &EG(uninitialized_zval);
			ZEND_VM_C_GOTO(free_and_exit_assign_obj);
		}
	}

ZEND_VM_C_LABEL(assign_object):
	if (OP2_TYPE == IS_CONST &&
	    EXPECTED(Z_OBJCE_P(object) == CACHED_PTR(opline->extended_value))) {
		void **cache_slot = CACHE_ADDR(opline->extended_value);
		uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1);
		zend_object *zobj = Z_OBJ_P(object);
		zval *property_val;

		if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) {
			property_val = OBJ_PROP(zobj, prop_offset);
			if (Z_TYPE_P(property_val) != IS_UNDEF) {
				zend_property_info *prop_info = (zend_property_info*) CACHED_PTR_EX(cache_slot + 2);

				if (UNEXPECTED(prop_info != NULL)) {
					value = zend_assign_to_typed_prop(prop_info, property_val, value EXECUTE_DATA_CC);
					ZEND_VM_C_GOTO(free_and_exit_assign_obj);
				} else {
ZEND_VM_C_LABEL(fast_assign_obj):
					value = zend_assign_to_variable(property_val, value, OP_DATA_TYPE, EX_USES_STRICT_TYPES());
					if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
						ZVAL_COPY(EX_VAR(opline->result.var), value);
					}
					ZEND_VM_C_GOTO(exit_assign_obj);
				}
			}
		} else {
			if (EXPECTED(zobj->properties != NULL)) {
				if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
					if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
						GC_DELREF(zobj->properties);
					}
					zobj->properties = zend_array_dup(zobj->properties);
				}
				property_val = zend_hash_find_ex(zobj->properties, Z_STR_P(property), 1);
				if (property_val) {
					ZEND_VM_C_GOTO(fast_assign_obj);
				}
			}

			if (!zobj->ce->__set) {

				if (EXPECTED(zobj->properties == NULL)) {
					rebuild_object_properties(zobj);
				}
				if (OP_DATA_TYPE == IS_CONST) {
					if (UNEXPECTED(Z_OPT_REFCOUNTED_P(value))) {
						Z_ADDREF_P(value);
					}
				} else if (OP_DATA_TYPE != IS_TMP_VAR) {
					if (Z_ISREF_P(value)) {
						if (OP_DATA_TYPE == IS_VAR) {
							zend_reference *ref = Z_REF_P(value);
							if (GC_DELREF(ref) == 0) {
								ZVAL_COPY_VALUE(&tmp, Z_REFVAL_P(value));
								efree_size(ref, sizeof(zend_reference));
								value = &tmp;
							} else {
								value = Z_REFVAL_P(value);
								Z_TRY_ADDREF_P(value);
							}
						} else {
							value = Z_REFVAL_P(value);
							Z_TRY_ADDREF_P(value);
						}
					} else if (OP_DATA_TYPE == IS_CV) {
						Z_TRY_ADDREF_P(value);
					}
				}
				zend_hash_add_new(zobj->properties, Z_STR_P(property), value);
				if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
					ZVAL_COPY(EX_VAR(opline->result.var), value);
				}
				ZEND_VM_C_GOTO(exit_assign_obj);
			}
		}
	}

	if (OP_DATA_TYPE == IS_CV || OP_DATA_TYPE == IS_VAR) {
		ZVAL_DEREF(value);
	}

	value = Z_OBJ_HT_P(object)->write_property(object, property, value, (OP2_TYPE == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL);

ZEND_VM_C_LABEL(free_and_exit_assign_obj):
	if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
		ZVAL_COPY(EX_VAR(opline->result.var), value);
	}
	FREE_OP_DATA();
ZEND_VM_C_LABEL(exit_assign_obj):
	FREE_OP2();
	FREE_OP1_VAR_PTR();
	/* assign_obj has two opcodes! */
	ZEND_VM_NEXT_OPCODE_EX(1, 2);
}