#    This file is part of Metasm, the Ruby assembly manipulation suite
#    Copyright (C) 2006-2009 Yoann GUILLOT
#
#    Licence is LGPL, see LICENCE in the top-level directory


# computes the difference beetween two ruby objects
# walks accessors, arrays and hashes

def Object.diff(o1, o2)
	if o1.class == o2.class
		h = {}
		case o1
		when Array, Hash
			if o1.kind_of? Array
				keys = (0...[o1.length, o2.length].max).to_a
			else
				keys = o1.keys | o2.keys
			end
			keys.each { |k|
				d = diff(o1[k], o2[k])
				h["[#{k.inspect}]"] = d if not d.empty?
			}
		else
			a = ($diff_accessor_cache ||= {})[o1.class] ||= (
				im = o1.class.public_instance_methods.map { |m| m.to_s }.grep(/^[a-z]/)
				(im & im.map { |m| m+'=' }).map { |m| m.chop }.find_all { |m| o1.instance_variable_get('@'+m) }
			)
			if a.empty?
				return o1 == o2 ? h : [o1, o2]
			end
			a.each { |k|
				d = diff(o1.send(k), o2.send(k))
				h['.' + k] = d if not d.empty?
			}
		end

		# simplify tree
		h.keys.each { |k|
			if h[k].kind_of? Hash and h[k].length == 1
				v = h.delete k
				h[k + v.keys.first] = v.values.first
			end
		}

		h
	else
		[o1, o2]
	end
end
