note
	description: "Execution recorder"
	status: "See notice at end of class."
	legal: "See notice at end of class."
	date: "$Date: 2017-04-12 13:15:47 +0000 (Wed, 12 Apr 2017) $"
	revision: "$Revision: 100150 $"

class 
	RT_DBG_EXECUTION_RECORDER

inherit
	RT_DBG_COMMON

	RT_DBG_INTERNAL
		export
			{NONE} all
		end

create {RT_EXTENSION}
	make

feature {NONE} -- Initialization

	make (p: separate RT_DBG_EXECUTION_PARAMETERS)
			-- Creation of Current object
		require
			p_attached: p /= Void
		do
			update_parameters (p)
		end
	
feature {RT_EXTENSION} -- Change

	update_parameters (p: separate RT_DBG_EXECUTION_PARAMETERS)
		require
			p_attached: p /= Void
		do
			maximum_record_count := p.maximum_record_count
			flatten_when_closing := p.flatten_when_closing
			keep_calls_records := p.keep_calls_records
			recording_values := p.recording_values
		end

	start_recording (ref: ANY; cid: INTEGER_32; fid: INTEGER_32; dep: INTEGER_32; a_break_index: INTEGER_32)
			-- Start recording and
			-- Initialize recording with effective information.
		require
			ref_attached: ref /= Void
			top_callstack_record_is_void: top_callstack_record = Void
			bottom_callstack_record_is_void: bottom_callstack_record = Void
		local
			r: like bottom_callstack_record
		do
			record_count := 0
			bottom_callstack_record := Void
			top_callstack_record := Void
			check
				callstack_records_are_void: top_callstack_record = Void and bottom_callstack_record = Void
			end
			debug ("rt_dbg_record")
				print ("Start_recording  (")
				Io.put_string ("ref=")
				Io.put_string (($ref).out)
				Io.put_string (",cid=")
				Io.put_integer (cid)
				Io.put_string (",fid=")
				Io.put_integer (fid)
				Io.put_string (",dep=")
				Io.put_integer (dep)
				Io.put_string (",break_index=")
				Io.put_integer (a_break_index)
				Io.put_string (")%N")
			end
			create r.make (Current, ref, cid, fid, dep)
			r.set_breakable_info ([a_break_index, 0])
			bottom_callstack_record := r
			top_callstack_record := r
		ensure
			bottom_callstack_record_is_not_void: bottom_callstack_record /= Void
			top_callstack_record_is_bottom: top_callstack_record = bottom_callstack_record
		end

	clear_recording_data
			-- Clear recording data when stopping recording
			-- or when leaving the recorded area (on leave_feature)
		do
			top_callstack_record := Void
			bottom_callstack_record := Void
			record_count := 0
		end

	stop_recording
			-- Stop recording (and clean data)
		do
			clear_recording_data
			if attached replay_stack as r then
				r.wipe_out
				replay_stack := Void
			end
			last_replay_operation_failed := False
			debug ("rt_dbg_record")
				print ("Stop_recording  %N")
			end
		ensure
			no_callstack_record: top_callstack_record = Void and bottom_callstack_record = Void
		end
	
feature -- Optimization properties

	record_count: INTEGER_32
			-- Number of field records.

	maximum_record_count: INTEGER_32
			-- Maximum number of records.
			-- 0 stands for no limit.

	flatten_when_closing: BOOLEAN
			-- Option: flatten record when closing

	keep_calls_records: BOOLEAN
			-- Option: keep calls record even when flattening calls

	recording_values: BOOLEAN
			-- Option: record values ?
	
feature -- Properties

	top_callstack_record: detachable like callstack_record
			-- Current top callstack record.

	bottom_callstack_record: detachable like callstack_record
			-- Bottom (or root) callstack record.
	
feature -- Access

	callstack_record (dep: INTEGER_32): detachable RT_DBG_CALL_RECORD
			-- Call stack record with depth dep
		local
			i: INTEGER_32
		do
			Result := top_callstack_record
			if dep < 0 then
				from
					i := dep
				until
					Result = Void or i = -1
				loop
					Result := Result.parent
					i := i + 1
				end
			else
				from
				until
					Result = Void or else Result.depth = dep
				loop
					Result := Result.parent
				end
				if Result /= Void and then Result.depth /= dep then
					Result := Void
				end
			end
			debug ("rt_dbg_replay")
				if Result /= Void then
					print ("callstack_record (" + dep.out + ") -> Result=" + Result.to_string (0) + "%N")
				end
			end
		end

	callstack_record_by_id (a_id: STRING_8): like callstack_record
			-- Call record for a_id
		require
			a_id_attached: a_id /= Void
		local
			p: INTEGER_32
			i: INTEGER_32
			r: like callstack_record
			sub_id: detachable STRING_8
		do
			debug ("rt_dbg_replay")
				print ("callstack_record_by_id (" + a_id + ") -start-%N")
			end
			p := a_id.index_of ('.', 1)
			if p > 0 then
				sub_id := a_id.substring (p + 1, a_id.count)
				i := a_id.substring (1, p - 1).to_integer_32
			else
				i := a_id.to_integer_32
			end
			r := callstack_record (i)
			if sub_id = Void then
				Result := r
			elseif r /= Void then
				Result := r.call_by_id (sub_id)
			end
			debug ("rt_dbg_replay")
				print ("callstack_record_by_id (" + a_id + ") -end-%N")
			end
		end
	
feature -- Constants from eif_debug.h

	Direction_back: INTEGER_32 = 1
			-- previous call

	Direction_forth: INTEGER_32 = 2
			-- next call

	Direction_left: INTEGER_32 = 3
			-- previous instruction

	Direction_right: INTEGER_32 = 4
			-- next intruction
	
feature -- Event

	enter_feature (ref: ANY; cid, fid: INTEGER_32; dep: INTEGER_32)
			-- Enter feature {cid}.fid on object ref, depth is dep
		require
			ref_attached: ref /= Void
			is_not_replaying: not is_replaying
			cid_positive: cid >= 0
		local
			r: RT_DBG_CALL_RECORD
		do
			debug ("rt_dbg_record")
				dtrace_indent (dep)
				dtrace ("enter_feature (")
				dtrace (ref.generating_type.name.as_string_8)
				dtrace (", " + cid.out + ", " + fid.out + ", " + dep.out + ")")
				dtrace (" [[" + record_count.out + "]]%N")
			end
			monitor_record_count
			create r.make (Current, ref, cid, fid, dep)
			increment_records_count (1)
			if attached top_callstack_record as topr then
				if topr.depth = 0 or else topr.depth = r.depth - 1 then
					r.attach_to (topr)
				else
					debug ("rt_dbg_warning")
						print ("Warning: enter mismatch !!!%N")
						print (" top: depth=" + r.depth.out + " ->" + r.debug_output + "%N")
						print (" now: depth=" + dep.out)
						print (" obj=" + ref.generating_type.name)
						print (" cid=" + cid.out)
						print (" fid=" + fid.out)
						print ("%N")
						print ("%N")
					end
					if topr.depth = r.depth then
						topr.deep_close
					end
					if attached {like callstack_record} callstack_record (r.depth - 1) as depr then
						r.attach_to (depr)
					else
						check
							should_not_occur: False
						end
					end
				end
			end
			top_callstack_record := r
			check
					top_callstack_record /= Void
			end
			if bottom_callstack_record = Void then
				bottom_callstack_record := top_callstack_record
			end
		end

	enter_rescue (ref: ANY; cid, fid: INTEGER_32; dep: INTEGER_32)
			-- Enter rescue on object ref, depth is dep
		require
			ref_attached: ref /= Void
			is_not_replaying: not is_replaying
		local
			r: detachable RT_DBG_CALL_RECORD
		do
			debug ("rt_dbg_record")
				dtrace_indent (dep)
				dtrace ("enter_rescue (")
				dtrace (ref.generating_type.name.as_string_8)
				dtrace (", " + dep.out + ")")
				dtrace (" [[" + record_count.out + "]]%N")
			end
			from
				r := top_callstack_record
			until
				r = Void or else r.depth = dep
			loop
				r := r.parent
			end
			if r = Void then
				debug ("rt_dbg_record")
					dtrace ("Error: enter_rescue -> No record !!%N")
				end
				stop_recording
				start_recording (ref, cid, fid, dep, 0)
			else
				check
					same_depth: r.depth = dep
				end
				top_callstack_record := r
				r.close_call_records
				top_callstack_record := r
			end
		end

	leave_feature (ref: ANY; cid, fid: INTEGER_32; dep: INTEGER_32)
			-- Leave feature {cid}.fid on object ref, depth is dep
		require
			ref_attached: ref /= Void
			is_not_replaying: not is_replaying
		local
			n: like callstack_record
		do
			debug ("rt_dbg_record")
				dtrace_indent (dep)
				dtrace ("leave_feature (")
				dtrace (ref.generating_type.name + " <" + ($ref).out + ">".as_string_8)
				dtrace (", " + cid.out + ", " + fid.out + ", " + dep.out + "). %N")
			end
			if not attached {like callstack_record} top_callstack_record as r then
			else
				if r = bottom_callstack_record then
					check
						parent_is_void: r.parent = Void
					end
					clear_recording_data
				else
					n := r
					if r.depth > dep then
						debug ("rt_dbg_warning")
							print ("Warning: leave mismatch !!!%N")
							print (" top: depth=" + r.depth.out + " ->" + r.debug_output + "%N")
							print (" now: depth=" + dep.out)
							print (" obj=" + ref.generating_type.name)
							print (" cid=" + cid.out)
							print (" fid=" + fid.out)
							print ("%N")
							print ("%N")
						end
						from
						until
							n = Void or else n.depth = dep
						loop
							n := n.parent
						end
						if n /= Void then
							r.deep_close_until (n)
							n.close_call_records
						end
					end
					if n = Void then
						debug ("rt_dbg_record")
							dtrace ("Error: leave_feature -> No associated record !!%N")
						end
						check
							should_not_occur: False
						end
						clear_recording_data
					else
						check
							same_dep: n.depth = dep
							same_object_type: n.same_object_type (ref)
							same_reference: not n.is_expanded implies n.object = ref
							same_cid: n.class_type_id = cid
							same_fid: n.feature_rout_id = fid
						end
						n.close
						top_callstack_record := n.parent
					end
				end
			end
		end

	notify_rt_hook (dep: INTEGER_32; bp_i, bp_ni: INTEGER_32)
			-- Notify RT_HOOK (bp_i) or RTNHOOK (bp_i, bp_ni)
		require
			is_not_replaying: not is_replaying
		do
			if attached {like callstack_record} top_callstack_record as r then
				if r.depth = dep then
					r.register_position (bp_i, bp_ni)
				else
					debug ("rt_dbg_warning")
						print ("Warning: hook mismatch on depth now=" + dep.out + " top.depth=" + r.depth.out + "%N")
					end
				end
			end
		end

	notify_rt_assign_attribute (a_dep: INTEGER_32; ref: ANY; a_offset: INTEGER_32; a_type: NATURAL_32; a_xpm: INTEGER_32)
			-- Notify variable assignment
			-- a_xpm contains information about expanded, precompiled, melted
		require
			ref_attached: ref /= Void
			is_not_replaying: not is_replaying
			top_call_stack_record_not_void: top_callstack_record /= Void
			valid_xpm_value: valid_xpm_value (a_xpm)
		do
			if attached top_callstack_record as t then
				check
					same_associated_depth: a_dep = t.depth
				end
				if attached {RT_DBG_VALUE_RECORD} object_attribute_record (a_offset, a_type, ref) as l_record then
					debug ("rt_dbg_record")
						print ("Att Assign=> " + l_record.debug_output + "%N")
					end
					t.add_value_record (l_record)
				end
			else
				debug ("rt_dbg_record")
					print ("Att Assign=> ERROR, no top_callstack_record !%N")
				end
				check
					should_not_occur: False
				end
			end
		end

	notify_rt_assign_local (a_dep: INTEGER_32; a_position: INTEGER_32; a_type: NATURAL_32; a_xpm: INTEGER_32)
			-- Notify variable assignment
			-- a_xpm contains information about expanded, precompiled, melted
		require
			is_not_replaying: not is_replaying
			top_call_stack_record_not_void: top_callstack_record /= Void
			valid_xpm_value: valid_xpm_value (a_xpm)
		do
			if attached top_callstack_record as t then
				debug ("rt_dbg_warning")
					if a_dep /= t.depth and t.depth /= 0 then
						print ("Loc Assign: dep=" + a_dep.out + "; pos=" + a_position.out + " -> depth mismatch %N")
					end
				end
				check
					same_associated_depth: a_dep = t.depth
				end
				if attached {RT_DBG_VALUE_RECORD} object_local_record (a_dep, a_position, a_type) as l_record then
					debug ("rt_dbg_record")
						print ("Loc Assign=> " + l_record.debug_output + "%N")
					end
					t.add_value_record (l_record)
				end
			else
				debug ("rt_dbg_record")
					print ("Loc Assign=> ERROR, no top_callstack_record !%N")
				end
				check
					should_not_occur: False
				end
			end
		end
	
feature -- Helpers

	valid_xpm_value (a_xpm: INTEGER_32): BOOLEAN
			-- Is a_xpm a valid xpm value ?
		local
			x, p, m: BOOLEAN
		do
			x := xpm_to_is_expanded (a_xpm)
			p := xpm_to_is_precompiled (a_xpm)
			m := xpm_to_is_melted (a_xpm)
			Result := a_xpm = x.to_integer + (p.to_integer |<< 1) + (m.to_integer |<< 2)
		end

	xpm_to_is_expanded (a_xpm: INTEGER_32): BOOLEAN
			-- Does a_xpm say is expanded ?
		do
			Result := (a_xpm & 1).to_boolean
		end

	xpm_to_is_precompiled (a_xpm: INTEGER_32): BOOLEAN
			-- Does a_xpm say is precompiled ?
		do
			Result := ((a_xpm & 2) |>> 1).to_boolean
		end

	xpm_to_is_melted (a_xpm: INTEGER_32): BOOLEAN
			-- Does a_xpm say is melted ?
		do
			Result := ((a_xpm & 4) |>> 2).to_boolean
		end
	
feature -- Replay

	activate_replay (b: BOOLEAN)
			-- Activate or deactive replay mode according to b
		do
			if b then
				if is_replaying then
				else
					replayed_call := top_callstack_record
					is_replaying := True
				end
			else
				if is_replaying then
					revert_replay_stack
					check
							replay_stack = Void
					end
					is_replaying := False
					replayed_call := Void
				end
			end
		ensure
			b_set: b and is_replaying
		end

	replay (dir: INTEGER_32; nb: INTEGER_32)
			-- Replay execution nb steps in direction dir
		do
			inspect dir
			when Direction_back then
				replay_back
			when Direction_forth then
				replay_forth
			when Direction_left then
				replay_left
			when Direction_right then
				replay_right
			else
			end
		end

	replay_to_point (a_id: STRING_8): BOOLEAN
			-- Replay execution to point identified by a_id
		require
			a_id_attached: a_id /= Void
		local
			d1, d2: INTEGER_32
		do
			debug ("rt_dbg_replay")
				print ("replay_to_point (" + a_id + ") -start-%N")
			end
			if attached replayed_call as l_curr and then attached {like callstack_record_by_id} callstack_record_by_id (a_id) as l_req and then attached {like callstack_record} l_req.associated_replayable_call as l_next then
				d1 := l_curr.depth
				d2 := l_next.depth
				if d2 > d1 then
					from
					until
						d1 >= d2
					loop
						replay_forth
						d1 := d1 + 1
						check
							valid_replayed_depth: is_call_at_depth (replayed_call, d1)
						end
					end
					check
						valid_replayed_depth: is_call_at_depth (replayed_call, d2)
					end
				elseif d2 < d1 then
					from
					until
						d1 <= d2
					loop
						replay_back
						d1 := d1 - 1
						check
							valid_replayed_depth: is_call_at_depth (replayed_call, d1)
						end
					end
					check
						valid_replayed_depth: is_call_at_depth (replayed_call, d2)
					end
				end
				Result := True
			end
			debug ("rt_dbg_replay")
				print ("replay_to_point (" + a_id + ") -end- => " + Result.out + "%N")
			end
		end

	replay_query (dir: INTEGER_32): INTEGER_32
			-- Replay execution nb steps in direction dir
		do
			inspect dir
			when Direction_back then
				if bottom_callstack_record /= Void and attached replayed_call as rc then
					Result := rc.available_calls_to_bottom
				end
			when Direction_forth then
				if attached replay_stack as rs then
					Result := rs.count
				end
			when Direction_left then
			when Direction_right then
			else
			end
		end

	replayed_call_details: detachable STRING_8
			-- Details for replayed_call
		require
			is_replaying: is_replaying
		do
			if attached replayed_call as r then
				debug ("rt_dbg_replay")
					print ("replayed_call_details -> " + r.to_string (0) + "%N")
				end
				Result := r.to_string (0)
			else
				debug ("rt_dbg_replay")
					print ("replayed_call_details -> None !%N")
				end
			end
		end

	callstack_record_details (a_id: STRING_8; nb: INTEGER_32): detachable STRING_8
			-- Details for callstack identified by a_id
			-- get information for nb levels.
		require
			a_id_attached: a_id /= Void
			is_replaying: is_replaying
		do
			debug ("rt_dbg_replay")
				print ("callstack_record_details (" + a_id + "," + nb.out + ") -start- %N")
			end
			if attached {like callstack_record_by_id} callstack_record_by_id (a_id) as r then
				Result := r.to_string (nb)
			end
			debug ("rt_dbg_replay")
				print ("callstack_record_details (" + a_id + "," + nb.out + ") -end- %N")
				if Result = Void then
					print ("%T -> Not Found")
				else
					print ("%T -> Result=" + Result)
				end
				print ("%N")
			end
		end
	
feature -- Monitoring

	monitor_record_count
			-- Monitor if record_count is not over max_record_count
			-- if max_record_count is 0, then no limit
		local
			bt: like bottom_callstack_record
			p: like bottom_callstack_record
			c, m, n: INTEGER_32
		do
			m := maximum_record_count
			if m > 0 then
				c := record_count
				if c.to_double > 1.1 * m.to_double then
					bt := bottom_callstack_record
					if top_callstack_record /= bt then
						debug ("rt_dbg_optimization")
							print ("monitor_record_count[" + ((m - c).abs // m).out + "%%] -> remove oldest: record_count=" + c.out + " (max=" + m.out + ")%N")
						end
						from
							p := top_callstack_record
						until
							p = Void or else p.parent = bt
						loop
							p := p.parent
						end
						check
							p_not_void_and_last_of_parent: p /= Void and then bt /= Void and then bt.is_last_call_record (p)
						then
							n := bt.record_count_but (p)
							p.remove_parent
						end
						bottom_callstack_record := p
						bt := p
						c := c - n
						record_count := c
						check
							same_record_count: bt /= Void and then record_count = bt.record_count_but (Void)
						end
						if c.to_double <= 0.9 * m.to_double then
							monitor_record_count
						end
						debug ("rt_dbg_optimization")
							print ("monitor_record_count -> count=" + c.out + "%N")
						end
					end
				end
			end
		end
	
feature -- Replay operation

	is_replaying: BOOLEAN
			-- Is replaying

	last_replay_operation_failed: BOOLEAN
			-- Does last replay operation failed ?

	replayed_call: detachable RT_DBG_CALL_RECORD
			-- Current replayed call stack record

	replay_stack_not_empty: BOOLEAN
			-- Is replay_stack non empty?
		do
			Result := attached replay_stack as rs and then not rs.is_empty
		end

	replay_stack: detachable LINKED_LIST [TUPLE [call_record: RT_DBG_CALL_RECORD; chgs: detachable ARRAYED_LIST [TUPLE [record: RT_DBG_VALUE_RECORD; backup: RT_DBG_VALUE_RECORD]]]]
			-- Replay operation stacks.
			-- useful to "revert" the replay steps.

	replay_back
			-- Replay execution back
		require
			is_replaying: is_replaying
			replayed_call_attached: replayed_call /= Void
		local
			rs: like replay_stack
			n: detachable RT_DBG_CALL_RECORD
			l_records: like changes_between
			chgs: detachable ARRAYED_LIST [TUPLE [record: RT_DBG_VALUE_RECORD; backup: RT_DBG_VALUE_RECORD]]
		do
			debug ("rt_dbg_replay")
				print ("replay_back -start- %N")
			end
			if attached {RT_DBG_CALL_RECORD} replayed_call as r then
				if attached {RT_DBG_CALL_RECORD} r.parent as p then
					last_replay_operation_failed := False
					if attached replay_stack as ot_rs then
						rs := ot_rs
						check
							replay_stack_not_empty: rs.count > 0
						end
						n := rs.last.call_record
					else
						create rs.make
						replay_stack := rs
						n := Void
					end
					l_records := changes_between (r, n)
					if not last_replay_operation_failed then
						create chgs.make (l_records.count)
						from
							l_records.finish
						until
							l_records.before
						loop
							debug ("rt_dbg_replay")
								print ("replay_back -> " + l_records.item_for_iteration.debug_output + " %N")
							end
							if attached l_records.item_for_iteration as ot_rec then
								if attached ot_rec.current_value_record as val then
									chgs.extend ([ot_rec, val])
									ot_rec.restore (val)
								else
									check
										should_not_occur: False
									end
								end
							end
							l_records.back
						end
						rs.extend ([r, chgs])
						replayed_call := p
					end
				else
					last_replay_operation_failed := True
				end
			else
				last_replay_operation_failed := True
			end
			debug ("rt_dbg_replay")
				print ("replay_back -end- %N")
			end
		ensure
			replayed_call_attached: replayed_call /= Void
		end

	replay_left
			-- Replay left (up in the current call stack element)
		require
			is_replaying: is_replaying
			replayed_call_attached: replayed_call /= Void
		local
			n: like replayed_call
			chgs: detachable ARRAYED_LIST [TUPLE [record: RT_DBG_VALUE_RECORD; backup: RT_DBG_VALUE_RECORD]]
			r_pos_line: INTEGER_32
			done: BOOLEAN
			rs: like replay_stack
		do
			debug ("rt_dbg_replay")
				print ("replay_left -start- %N")
			end
			if attached replayed_call as r then
				from
					r_pos_line := r.replayed_position.line
					if attached replay_stack as ot_rs then
						rs := ot_rs
					else
						create rs.make
						replay_stack := rs
					end
				until
					done
				loop
					if attached r.left_step as ot_records then
						last_replay_operation_failed := False
						create chgs.make (ot_records.count)
						from
							ot_records.finish
						until
							ot_records.before
						loop
							debug ("rt_dbg_replay")
								print ("replay_left -> " + ot_records.item_for_iteration.debug_output + " %N")
							end
							if attached ot_records.item_for_iteration as ot_rec then
								if attached ot_rec.current_value_record as val then
									chgs.extend ([ot_rec, val])
									ot_rec.restore (val)
								else
									check
										should_not_occur: False
									end
								end
							end
							ot_records.back
						end
						rs.extend ([r, chgs])
					else
						if r.parent = Void then
							r.revert_left_step
						else
							replay_back
						end
						done := True
					end
					n := replayed_call
					check
						n_attached: n /= Void
					end
					if not done then
						done := n /= r or else n = Void or else (n.replayed_position.line /= r_pos_line)
					end
				end
			end
			debug ("rt_dbg_replay")
				print ("replay_left -end- %N")
			end
		ensure
			replayed_call_attached: replayed_call /= Void
		end

	replay_left_to_first
			-- Replay execution left as many times as needed to be at the entry of the feature
		local
			r, prev: like replayed_call
		do
			from
				r := replayed_call
			until
				r = Void or else r.replayed_position_is_first or else prev = r
			loop
				prev := r
				replay_left
				r := replayed_call
			end
		end

	replay_forth
			-- Replay execution forth
		require
			is_replaying: is_replaying
			replayed_call_not_void: replayed_call /= Void
			replay_stack_not_empty: replay_stack_not_empty
		local
			n: RT_DBG_CALL_RECORD
			t: TUPLE [call_record: RT_DBG_CALL_RECORD; chgs: detachable ARRAYED_LIST [TUPLE [record: RT_DBG_VALUE_RECORD; backup: RT_DBG_VALUE_RECORD]]]
			done: BOOLEAN
		do
			debug ("rt_dbg_replay")
				print ("replay_forth -start-%N")
			end
			if attached replay_stack as rs then
				check
						attached replayed_call as r
				then
					from
					until
						done
					loop
						check
								rs.count > 0
						end
						rs.finish
						t := rs.item_for_iteration
						rs.remove
						if rs.is_empty then
							replay_stack := Void
						end
						n := t.call_record
						if attached t.chgs as ot_chgs then
							from
								ot_chgs.finish
							until
								ot_chgs.before
							loop
								debug ("rt_dbg_replay")
									print ("replay_forth -> " + ot_chgs.item_for_iteration.record.debug_output + " %N")
								end
								if attached {TUPLE [record: RT_DBG_VALUE_RECORD; backup: RT_DBG_VALUE_RECORD]} ot_chgs.item_for_iteration as ch then
									ch.record.revert (ch.backup)
								end
								ot_chgs.back
							end
						end
						if n = r then
							r.revert_left_step
							done := replay_stack = Void
						else
							check
									r = n.parent
							end
							replayed_call := n
							done := True
						end
					end
				end
			else
				last_replay_operation_failed := True
			end
			debug ("rt_dbg_replay")
				print ("replay_forth -end-%N")
			end
		ensure
			replayed_call_attached: replayed_call /= Void
		end

	replay_right
			-- Replay right (down in the current call stack element)
		require
			is_replaying: is_replaying
			replayed_call_not_void: replayed_call /= Void
			replay_stack_not_empty: replay_stack_not_empty
		local
			n: RT_DBG_CALL_RECORD
			t: TUPLE [call_record: RT_DBG_CALL_RECORD; chgs: detachable ARRAYED_LIST [TUPLE [record: RT_DBG_VALUE_RECORD; backup: RT_DBG_VALUE_RECORD]]]
			r_pos_line: INTEGER_32
			done: BOOLEAN
		do
			debug ("rt_dbg_replay")
				print ("replay_right -start-%N")
			end
			if attached replay_stack as rs then
				check
						attached replayed_call as r
				then
					from
						r_pos_line := r.replayed_position.line
					until
						done
					loop
						check
							replay_stack_not_empty: replay_stack_not_empty
						end
						rs.finish
						t := rs.item_for_iteration
						rs.remove
						if rs.is_empty then
							replay_stack := Void
						end
						if attached t.chgs as ot_chgs then
							from
								ot_chgs.finish
							until
								ot_chgs.before
							loop
								debug ("rt_dbg_replay")
									print ("replay_right -> " + ot_chgs.index.out + ") " + ot_chgs.item_for_iteration.record.debug_output + " %N")
								end
								if attached {TUPLE [record: RT_DBG_VALUE_RECORD; backup: RT_DBG_VALUE_RECORD]} ot_chgs.item_for_iteration as ch then
									ch.record.revert (ch.backup)
								end
								ot_chgs.back
							end
						end
						n := t.call_record
						if n = r then
							n.revert_left_step
							done := n.replayed_position.line /= r_pos_line
						else
							check
									r = n.parent
							end
							if n.replayed_position.line = 0 then
								n.revert_left_step
							end
							replayed_call := n
							replay_left_to_first
							replayed_call := n
							done := True
						end
					end
				end
			else
				last_replay_operation_failed := True
			end
			debug ("rt_dbg_replay")
				print ("replay_right -end-%N")
			end
		ensure
			replayed_call_attached: replayed_call /= Void
		end

	revert_replay_stack
			-- Revert remaining replay stack
		require
			is_replaying: is_replaying
		local
			r: like replayed_call
			n: like callstack_record
			t: TUPLE [call_record: RT_DBG_CALL_RECORD; chgs: detachable ARRAYED_LIST [TUPLE [record: RT_DBG_VALUE_RECORD; backup: RT_DBG_VALUE_RECORD]]]
		do
			debug ("rt_dbg_replay")
				print ("revert_replay_stack -start-%N")
			end
			if attached replay_stack as rs then
				from
					r := replayed_call
				until
					rs.is_empty
				loop
					rs.finish
					t := rs.item_for_iteration
					rs.remove
					n := t.call_record
					if attached t.chgs as ot_chgs then
						from
							ot_chgs.finish
						until
							ot_chgs.before
						loop
							debug ("rt_dbg_replay")
								print ("revert_replay_stack -> " + ot_chgs.item_for_iteration.record.debug_output + " %N")
							end
							if attached {TUPLE [record: RT_DBG_VALUE_RECORD; backup: RT_DBG_VALUE_RECORD]} ot_chgs.item_for_iteration as ch then
								ch.record.revert (ch.backup)
							end
							ot_chgs.back
						end
					end
					if n = r then
						if r /= Void then
							r.revert_left_step
						end
					else
						check
								r = n.parent
						end
						replayed_call := n
					end
				end
				check
					empty_replay_stack: rs.is_empty
				end
			end
			replay_stack := Void
			debug ("rt_dbg_replay")
				print ("revert_replay_stack -end-%N")
			end
		end
	
feature -- Change

	increment_records_count (n: INTEGER_32)
			-- Incremente record_count by n
		do
			record_count := record_count + n
		ensure
			record_count_positive: record_count >= 0
		end
	
feature -- Measurement

	is_call_at_depth (a_call: like replayed_call; d: INTEGER_32): BOOLEAN
			-- Call a_call has depth d ?
		do
			Result := attached {like replayed_call} a_call as c and then c.depth = d
		end
	
note
	library: "EiffelBase: Library of reusable components for Eiffel."
	copyright: "Copyright (c) 1984-2017, Eiffel Software and others"
	license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
	source: "[
		Eiffel Software
		5949 Hollister Ave., Goleta, CA 93117 USA
		Telephone 805-685-1006, Fax 805-685-6869
		Website http://www.eiffel.com
		Customer support http://support.eiffel.com
	]"

end -- class RT_DBG_EXECUTION_RECORDER

Generated by ISE EiffelStudio