note description: "[Test case serializer]" author: "" date: "$Date$" revision: "$Revision$" class ITP_TEST_CASE_SERIALIZER inherit ITP_SHARED_CONSTANTS EQA_TEST_CASE_SERIALIZATION_UTILITY EXCEPTIONS rename exception as exp, class_name as class__name end ITP_TEST_CASE_SERIALIZATION_CONSTANTS create make feature{NONE} -- Initialization make (a_interpreter: like interpreter; a_post_state_serialized: BOOLEAN) -- Initialization `interpreter' with `a_interpreter'. -- `a_post_state_serialized' indicates if post-state information should -- be serialized as well. require a_interpreter_attached: a_interpreter /= Void do interpreter := a_interpreter create object_graph_traversor create test_case_hashs.make (2048) create state_counts.make (2048) state_counts.compare_objects test_case_hashs.compare_objects is_post_state_serialized := a_post_state_serialized ensure interpreter_set: interpreter = a_interpreter end feature -- Access interpreter: ITP_INTERPRETER -- AutoTest interpreter attached to current serializer test_case_hashs: HASH_TABLE [STRING, STRING] -- Table of hash codes for test cases seen so far. -- Key is hash code, value is the string representation of that hash code state_counts: HASH_TABLE [INTEGER, STRING] -- Table of counts for established pre-states for test cases -- Keys are state hash codes, values are the number of times those states have been -- seen. string_representation: TUPLE [serialization: STRING; states: STRING] -- String representation of the test case local i: INTEGER l_last: INTEGER l_lower, l_upper: INTEGER l_var_tbl: HASH_TABLE [INTEGER, INTEGER] l_var_id: INTEGER l_description: like pre_state_objects l_index: INTEGER l_object: detachable ANY l_should_serialize: BOOLEAN l_serialization: STRING l_states: STRING l_state_count: INTEGER l_id: STRING l_state_hash: STRING l_idx: INTEGER l_hash: STRING l_test_case_id: STRING l_tc_signature: STRING do if is_test_case_setup then if attached test_case_hash_code as l_test_case_hash_code then l_hash := l_test_case_hash_code.twin else -- should not happen, this branch is to make void-safety happy. l_hash := "" end l_idx := l_hash.index_of ('#', 1) l_state_hash := l_hash.substring (l_idx + 1, l_hash.count) if interpreter.is_failing_test_case then -- For failing test cases, ID = recipient_class.recipient.exception_code.tag l_test_case_id := interpreter.last_fault_id else -- For passing test cases, ID = class_under_test.feature_under_test.0.noname l_test_case_id := l_hash.substring (1, l_idx - 1) + ".0.noname" end l_tc_signature := l_test_case_id + "#" + l_state_hash -- Update state counts. state_counts.search (l_state_hash) if state_counts.found then l_state_count := state_counts.found_item + 1 state_counts.replace (l_state_count, l_state_hash) else l_state_count := 1 state_counts.force (1, l_state_hash) end create l_states.make (1024) create l_id.make (128) if attached class_name as l_class_name then l_id.append (l_class_name) end l_id.append_character ('.') l_id.append (feature_name) l_id.append_character ('.') l_id.append_integer (l_state_hash.hash_code) l_states.append (once "") l_states.append (l_id) l_states.append (once " count=") l_states.append_integer (state_counts.item (l_state_hash)) l_states.append (once "; fault=") l_states.append_boolean (exception /= Void and then not exception.is_empty) l_states.append (once "%N") -- This is a new state if l_state_count = 1 then l_states.append (once "%N") append_object_state (pre_state_object_summary, l_states, True) append_object_state (post_state_object_summary, l_states, False) l_states.append (once "%N") end l_should_serialize := interpreter.is_duplicated_test_case_serialized or else not test_case_hashs.has (l_tc_signature) if not l_should_serialize then l_serialization := "" else if not interpreter.is_duplicated_test_case_serialized then test_case_hashs.put (l_tc_signature, l_tc_signature) end -- Synthesize serialization part for a test case. create l_serialization.make (1024) l_serialization.append (once "%N") -- Synthesize time. append_time (time, l_serialization) if attached types as l_types then if attached operands as l_operands then if attached class_name as l_class_name then if attached feature_name as l_feature_name then -- Synthesize test case. append_test_case (l_class_name, l_feature_name, argument_count, is_creation, is_query, l_operands, l_types, l_serialization) end end end end if attached pre_state_objects as l_pre_state_objects then -- Synthesize all AutoTest created variables in current test case. append_all_variables (l_pre_state_objects, l_serialization) end -- Synthesize trace. append_exception_trace (exception, l_serialization) -- Synthesize hash. append_test_case_hash_code (l_tc_signature, l_serialization) -- Synthesize pre-/post-state object summary. append_object_state (pre_state_object_summary, l_serialization, True) append_object_state (post_state_object_summary, l_serialization, False) -- Synthesize serialization append_object_serialization (pre_state_serialization, l_serialization) l_serialization.append (once "%N") end else create l_serialization.make_empty create l_states.make_empty end Result := [l_serialization, l_states] ensure result_attached: Result /= Void end class_name: detachable STRING -- Class name of the test case feature_name: detachable STRING -- Feature name of the test case test_case_index: INTEGER -- Test case index time: INTEGER -- Time in millisecond relative to the starting of current test session operands: detachable SPECIAL [INTEGER] -- Object index of operands for the test case -- Target object is in the 0-th position, -- followed by arguments, if any, and then by result object, if any. -- Note: Strictly speaking, the result object is not an operand for the feature call. types: detachable SPECIAL [STRING] -- Type names of the `operands'. -- Type names appear in the same order as their corresponding objects in `operands'. argument_count: INTEGER -- Number of arguments in the feature with name `feature_name' exception: detachable STRING -- Exception trace of the last test case -- Can be empty if there was no exception -- or Void if the test case is not properly setup. feature -- Status report is_test_case_setup: BOOLEAN -- Is the last test case set by `setup_test_case' valid? is_creation: BOOLEAN -- Is feature with `feature_name' a creator? is_query: BOOLEAN -- Is feature with `feature_name' a query? is_post_state_serialized: BOOLEAN -- Should post-state information be serialized as well? -- Strictly speaking, post-state information is not necessary because -- when pre-state information is available, we can reexecute the test case -- to observe the post-state. But if post-state information is available, we -- don't need to re-execute the test case. feature -- Basic operations setup_test_case (a_test_case: detachable ANY) -- Setup information about the current test case. -- Set `is_test_case_setup' to True if `a_test_case' contain valid information of a test case, -- otherwise, set `is_test_case_setup' to False'. do -- Check if `a_test_case' contains correct information. if attached{detachable TUPLE [class_name: STRING; feature_name: STRING; test_case_index: INTEGER; time: INTEGER; operands: SPECIAL [INTEGER]; types: SPECIAL [STRING]; argument_count: INTEGER; is_creation: BOOLEAN; is_query: BOOLEAN]} a_test_case as l_tc then class_name := l_tc.class_name feature_name := l_tc.feature_name test_case_index := l_tc.test_case_index time := l_tc.time operands := l_tc.operands types := l_tc.types argument_count := l_tc.argument_count is_creation := l_tc.is_creation is_query := l_tc.is_query end -- Check if `a_test_case' contains information of a valid test case. if (class_name /= Void and then not class_name.is_empty) and then (feature_name /= Void and then not feature_name.is_empty) and then test_case_index > 0 and then (operands /= Void and then types /= Void and then operands.count = types.count) and then argument_count < operands.count and then (not (is_creation and is_query)) then set_is_test_case_valid (True) else set_is_test_case_valid (False) end end set_is_test_case_valid (b: BOOLEAN) -- Set `is_test_case_setup' with `b'. do is_test_case_setup := b ensure is_test_case_valid_set: is_test_case_setup = b end retrieve_pre_state -- Retrieve state of objects before test case -- execution. local l_data: detachable TUPLE [summary: STRING; hash: STRING] l_serialization: like object_serialization do pre_state_object_summary := Void test_case_hash_code := Void l_data := abstract_object_state (True) if l_data /= Void then pre_state_object_summary := l_data.summary test_case_hash_code := l_data.hash end l_serialization := object_serialization (True) if l_serialization /= Void then pre_state_serialization := l_serialization.serialization pre_state_objects := l_serialization.description else pre_state_serialization := Void pre_state_objects := Void end end retrieve_post_state (a_is_failing_test_case: BOOLEAN) -- Retrieve post state of the test case. -- `a_is_failing_test_case' indicates if the last test case is failing. local l_data: detachable TUPLE [summary: STRING; hash: STRING] do if is_test_case_setup then if interpreter.error_buffer /= Void then exception := interpreter.error_buffer.twin end if attached exception as l_exception then if l_exception.is_empty then if is_post_state_serialized and then interpreter.post_state_retrieveal_byte_code /= Void then interpreter.retrieve_post_object_state post_state_object_summary := Void l_data := abstract_object_state (False) if l_data /= Void then post_state_object_summary := l_data.summary end else post_state_object_summary := Void end else post_state_object_summary := Void end end else exception := Void end end object_serialization (a_pre_state: BOOLEAN): detachable TUPLE [serialization: detachable STRING; description: detachable HASH_TABLE [TUPLE [object: detachable ANY; type: STRING], INTEGER]] -- Object serialization for `operands'. `a_pre_state' indicates if the objects are in pre-execution or post-execution state. local l_lower: INTEGER l_upper: INTEGER l_any: detachable ANY l_retried: BOOLEAN do if not l_retried then if is_test_case_setup then if is_creation and then a_pre_state then l_lower := 1 else l_lower := 0 end l_upper := argument_count if is_query and then not a_pre_state then l_upper := l_upper + 1 end Result := objects_as_string (operands, l_lower, l_upper) -- Uncomment the following code to check if serialization is done correctly. -- l_any ?= deserialized_object (Result.serialization) -- if attached {SPECIAL [detachable ANY]} l_any as l_obj then -- interpreter.log_message ("Serialization: Deserialization correct.%N") -- else -- interpreter.log_message ("Serialization: Deserialization failed.") -- if l_any /= Void then -- interpreter.log_message (l_any.generating_type + "%N") -- else -- interpreter.log_message ("Void %N") -- end -- end else Result := Void end end rescue l_retried := True Result := Void retry end feature{NONE} -- Implementation pre_state_object_summary: detachable STRING -- Pre-state object summary post_state_object_summary: detachable STRING -- Post-state object summary test_case_hash_code: detachable STRING -- Hash code for current test case pre_state_serialization: detachable STRING -- String representing the serialized data for objects specified by -- `operands' in pre-execution state. pre_state_objects: detachable HASH_TABLE [TUPLE [object: detachable ANY; type: STRING], INTEGER] -- List of variables in `pre_state_serialization' along with their object index. -- Key is variable index, value is the variable object itself in pre-execution state. abstract_object_state (a_pre_state: BOOLEAN): detachable TUPLE [summary: STRING; hash: STRING] -- Retrieve object state summary from objects specified by `operands' -- `summary' is the state summary for `operands'. -- `hash' is the hash code of `summary'. -- `a_pre_state' indicates if those object states are retrieved before test case execution. local l_summary: like pre_state_object_summary l_hash_code: STRING l_queries: HASH_TABLE [STRING, STRING] l_cursor: CURSOR l_value_hash_list: LINKED_LIST [INTEGER] do if is_test_case_setup then create l_hash_code.make (64) -- Setup test case hash code, which consists the hash of the states of all operands. if a_pre_state then if attached class_name as l_class_name then l_hash_code.append (l_class_name) end l_hash_code.append_character ('.') if attached feature_name as l_feature_name then l_hash_code.append (l_feature_name) end l_hash_code.append_character ('#') end create l_summary.make (256) l_queries := interpreter.query_values l_cursor := l_queries.cursor from l_queries.start until l_queries.after loop l_summary.append_character ('%T') l_summary.append (l_queries.key_for_iteration) l_summary.append (query_value_separator) l_summary.append (l_queries.item_for_iteration) l_summary.append_character ('%N') l_queries.forth end l_queries.go_to (l_cursor) -- Calculate hash code of current test case for test case duplication detection. if a_pre_state then l_hash_code.append (hash_code_from_list (interpreter.query_value_hash_list).out) else l_hash_code.append ("0") end Result := [l_summary, l_hash_code] else Result := Void end end hash_code_from_list (a_list: LINKED_LIST [INTEGER]): INTEGER -- Hash code of `a_list'. do from a_list.start until a_list.after loop Result := ((Result \\ 8388593) |<< 8) + (a_list.item_for_iteration \\ 255) a_list.forth end end commented_string (a_string: STRING): STRING -- A commented version of `a_string', with every line lead by "--" require a_string_attached: a_string /= Void local l_lines: LIST [STRING] l_header: STRING l_line: STRING do l_header := "--" l_lines := a_string.split ('%N') create Result.make (a_string.count + 10) from l_lines.start until l_lines.after loop l_line := l_lines.item_for_iteration if not l_line.is_empty then l_line.prepend (l_header) l_line.extend ('%N') Result.append (l_line) end l_lines.forth end ensure result_attached: Result /= Void end feature{NONE} -- Implementation objects_as_string (a_objects: detachable SPECIAL [INTEGER]; a_lower: INTEGER; a_upper: INTEGER): TUPLE [serialization: STRING; description: like recursively_referenced_objects] -- Serialized version of objects whose ID are specified by `a_objects' starting from -- position `a_lower' and ending at position `a_upper'. -- `serialization' is the serialized stream for those objects. -- The serialized objects have the format: SPECIAL [detachable ANY] -- The number of elements in this SPECIAL is even, and partitioned into pairs. -- The first two elements are a pair, and the third and fourth elements are another pair, and so on. -- So it looks like this: [(object1_index, object1), (object2_index, object2), ... , (objectn_index, objectn)]. -- `description' describes what variables are in the stream and what their vairable IDs are. local l: SPECIAL [INTEGER] i: INTEGER l_obj_list: like recursively_referenced_objects l_objects: SPECIAL [detachable ANY] l_index: INTEGER l_object: detachable ANY l_type_name: STRING do -- Filter out unnecessary objects. create l.make_filled (0, a_upper - a_lower + 1) from i := a_lower until i > a_upper loop if a_objects /= Void then l.put (a_objects.item (i), i - a_lower) end i := i + 1 end -- Recursively traverse object graphs starting from objects given in `l'. if a_lower <= a_upper then -- interpreter.log_message ("Serialization: To-be-serialized objects: ") -- if pre_state_object_summary /= Void then -- interpreter.log_message (pre_state_object_summary) -- interpreter.log_message ("%N") -- end l_obj_list := recursively_referenced_objects (l) create l_objects.make_filled (Void, l_obj_list.count * 2) from i := 0 l_obj_list.start until l_obj_list.after loop l_index := l_obj_list.key_for_iteration l_object := l_obj_list.item_for_iteration.object l_objects.put (l_index, i) l_objects.put (l_object, i + 1) -- interpreter.log_message (l_index.out + ", ") -- if l_object /= Void then -- l_type_name := l_object.generating_type -- if l_type_name ~ "INTEGER_32" or l_type_name ~ "BOOLEAN" then -- interpreter.log_message ("(" + l_object.out + ")") -- end -- interpreter.log_message (l_type_name + ", ") -- else -- interpreter.log_message ("Void, ") -- end i := i + 2 l_obj_list.forth end -- interpreter.log_message ("%N") else create l_objects.make_filled (0, 1) create l_obj_list.make (0) end Result := [serialized_object (l_objects), l_obj_list] end recursively_referenced_objects (a_roots: SPECIAL [INTEGER]): HASH_TABLE [TUPLE [object: detachable ANY; type: STRING], INTEGER] -- Objects in `interpreter''s object pool that are recursively referenced by varibles whose IDs are -- specified by `a_roots'. Result is a list of such referenced object pairs. In each pair, `index' is the -- variable index in the object pool, `object' is the variable itself. -- Objects specified in `a_roots' are also included in Result. -- A pair [[Void, "NONE"], 1] is always included in Result. require a_roots_not_empty: a_roots.count > 0 local l_tbl: HASH_TABLE [TUPLE[object: detachable ANY; type: STRING], INTEGER] i: INTEGER c: INTEGER l_store: ITP_STORE l_object: detachable ANY l_traversor: like object_graph_traversor l_index: INTEGER do create l_tbl.make (20) -- Insert [Void, v_1], becuase AutoTest will always set v_1 to Void. l_tbl.put ([Void, "NONE"], 1) l_store := interpreter.object_store l_traversor := object_graph_traversor -- Iterate through all root objects. from i := 0 c := a_roots.count until i = c loop l_index := a_roots.item (i) l_object := l_store.variable_value (l_index) if l_object /= Void then -- For each root object, recursively traverse the whole object graph. l_tbl.put ([l_object, l_object.generating_type.name.out], l_index) l_traversor.wipe_out l_traversor.set_object_action (agent on_object_visited (?, l_tbl)) l_traversor.set_root_object (l_object) l_traversor.traverse else l_tbl.put ([Void, "NONE"], l_index) end i := i + 1 end Result := l_tbl end on_object_visited (a_object: detachable ANY; a_object_table: HASH_TABLE [TUPLE [detachable ANY, STRING], INTEGER]) -- Action to be performed when `a_object' is visited during object graph traversal. -- If `a_object' is found in the object pool in `interpreter', put it in `a_object_table'. -- Key of `a_object_table' is the object index in the object pool, value is the object itself. local l_interpreter: like interpreter l_index: INTEGER do l_interpreter := interpreter if attached {INTEGER} a_object as l_int then l_index := l_interpreter.object_store.variable_index (l_int) if l_index > 0 then -- interpreter.log_message ("Serialization: Found an integer%N") a_object_table.put ([l_int.item, l_int.generating_type.out], l_index) end elseif attached {BOOLEAN} a_object as l_bool then l_index := l_interpreter.object_store.variable_index (l_bool) if l_index > 0 then -- interpreter.log_message ("Serialization: Found boolean%N") a_object_table.put ([l_bool.item, l_bool.generating_type.out], l_index) end elseif attached{INTEGER_8} a_object as l_int8 then l_index := l_interpreter.object_store.variable_index (l_int8) if l_index > 0 then a_object_table.put ([l_int8.item, l_int8.generating_type.out], l_index) end elseif attached{INTEGER_16} a_object as l_int16 then l_index := l_interpreter.object_store.variable_index (l_int16) if l_index > 0 then a_object_table.put ([l_int16.item, l_int16.generating_type.out], l_index) end elseif attached{INTEGER_64} a_object as l_int64 then l_index := l_interpreter.object_store.variable_index (l_int64) if l_index > 0 then a_object_table.put ([l_int64.item, l_int64.generating_type.out], l_index) end elseif attached{NATURAL_8} a_object as l_nat8 then l_index := l_interpreter.object_store.variable_index (l_nat8) if l_index > 0 then a_object_table.put ([l_nat8.item, l_nat8.generating_type.out], l_index) end elseif attached{NATURAL_16} a_object as l_nat16 then l_index := l_interpreter.object_store.variable_index (l_nat16) if l_index > 0 then a_object_table.put ([l_nat16.item, l_nat16.generating_type.out], l_index) end elseif attached{NATURAL_32} a_object as l_nat32 then l_index := l_interpreter.object_store.variable_index (l_nat32) if l_index > 0 then a_object_table.put ([l_nat32.item, l_nat32.generating_type.out], l_index) end elseif attached{NATURAL_64} a_object as l_nat64 then l_index := l_interpreter.object_store.variable_index (l_nat64) if l_index > 0 then a_object_table.put ([l_nat64.item, l_nat64.generating_type.out], l_index) end elseif attached{CHARACTER_8} a_object as l_char8 then l_index := l_interpreter.object_store.variable_index (l_char8) if l_index > 0 then a_object_table.put ([l_char8.item, l_char8.generating_type.out], l_index) end elseif attached{CHARACTER_32} a_object as l_char32 then l_index := l_interpreter.object_store.variable_index (l_char32) if l_index > 0 then a_object_table.put ([l_char32.item, l_char32.generating_type.out], l_index) end elseif attached{REAL_32} a_object as l_real32 then l_index := l_interpreter.object_store.variable_index (l_real32) if l_index > 0 then a_object_table.put ([l_real32.item, l_real32.generating_type.out], l_index) end elseif attached{REAL_64} a_object as l_real64 then l_index := l_interpreter.object_store.variable_index (l_real64) if l_index > 0 then a_object_table.put ([l_real64.item, l_real64.generating_type.out], l_index) end -- elseif attached{POINTER} a_object as l_ptr then -- l_index := l_interpreter.object_store.variable_index (l_ptr) -- if l_index > 0 then -- a_object_table.put ([l_ptr.item, l_ptr.generating_type.out], l_index) -- end else l_index := l_interpreter.object_store.variable_index (a_object) if l_index > 0 then if a_object = Void then a_object_table.put ([Void, "NONE"], l_index) else a_object_table.put ([a_object, a_object.generating_type.out], l_index) end end end end object_graph_traversor: ITP_OBJECT_TRAVERSABLE -- Object graph traversor, used to find objects in the object pool -- that are also (recursively) referenced by a given object. feature{NONE} -- Implementation/Test case synthesis append_time (a_time: INTEGER; a_buffer: STRING) -- Append `a_time' into `a_buffer'. do a_buffer.append (time_tag_start) a_buffer.append (a_time.out) a_buffer.append (time_tag_end) a_buffer.append_character ('%N') end append_test_case (a_class_name: STRING; a_feature_name: STRING; a_argument_count: INTEGER; a_is_creation: BOOLEAN; a_is_query: BOOLEAN; a_operands: SPECIAL[INTEGER]; a_types: SPECIAL[STRING]; a_buffer: STRING) -- Append test case defined by `a_class_name', `a_feature_name', `a_is_creation', `a_is_query', `a_operands' and `a_types' into `a_buffer'. local l_last: INTEGER i: INTEGER l_var_id: INTEGER do l_last := a_operands.count - 1 -- Synthesize class. a_buffer.append (class_tag_start) a_buffer.append (a_class_name) a_buffer.append (class_tag_end) a_buffer.append_character ('%N') -- Synthesize test case body. a_buffer.append (code_tag_start) a_buffer.append_character ('%N') -- Synthesize return value. if a_is_query then a_buffer.append (once "v_") a_buffer.append (a_operands.item (l_last).out) a_buffer.append (once " := ") end if a_is_creation then a_buffer.append (once "create {") a_buffer.append (a_types.item (0)) a_buffer.append (once "}") end a_buffer.append (once "v_") a_buffer.append (a_operands.item (0).out) a_buffer.append_character ('.') a_buffer.append (a_feature_name) -- Synthesize arguments. if a_argument_count > 0 then a_buffer.append (once " (") from i := 1 until i > a_argument_count loop a_buffer.append (once "v_") a_buffer.append (a_operands.item (i).out) if i < a_argument_count then a_buffer.append (once ", ") end i := i + 1 end a_buffer.append (once ")") end a_buffer.append_character ('%N') a_buffer.append (code_tag_end) a_buffer.append_character ('%N') -- Synthesize type information. a_buffer.append (operands_tag_start) a_buffer.append_character ('%N') from i := 0 until i > l_last loop if attached types as l_types then append_variable_with_type (a_operands.item (i), l_types.item (i), a_buffer, True) end i := i + 1 end a_buffer.append (operands_tag_end) a_buffer.append_character ('%N') end append_all_variables (a_objects: HASH_TABLE [TUPLE[object: detachable ANY; type: STRING], INTEGER_32]; a_buffer: STRING) -- Append variable descriptions in `a_objects' into `a_buffer'. -- `a_objects' is a hash table, key is variable index in object pool, value is the object itself. local l_index: INTEGER l_object: detachable ANY l_type_name: STRING do a_buffer.append (all_variables_tag_start) a_buffer.append_character ('%N') if a_objects /= Void then from a_objects.start until a_objects.after loop l_index := a_objects.key_for_iteration l_object := a_objects.item_for_iteration.object l_type_name := a_objects.item_for_iteration.type; if l_object = Void then append_variable_with_type (l_index, once "NONE", a_buffer, True) else append_variable_with_type (l_index, l_type_name, a_buffer, True) end a_objects.forth end end a_buffer.append (all_variables_tag_end) a_buffer.append_character ('%N') end append_exception_trace (a_trace: detachable STRING; a_buffer: STRING) -- Append `a_trace' into `a_buffer'. do a_buffer.append (trace_tag_start) a_buffer.append_character ('%N') a_buffer.append (cdata_tag_start) a_buffer.append_character ('%N') if attached exception as l_exp then a_buffer.append (l_exp) end a_buffer.append_character ('%N') a_buffer.append (cdata_tag_end) a_buffer.append_character ('%N') a_buffer.append (trace_tag_end) a_buffer.append_character ('%N') end append_test_case_hash_code (a_hash_code: STRING; a_buffer: STRING) -- Append test case hash code `a_hash_code' in `a_buffer'. do a_buffer.append (hash_code_tag_start) a_buffer.append (a_hash_code) a_buffer.append (hash_code_tag_end) a_buffer.append_character ('%N') end append_object_serialization (a_serialization: detachable STRING; a_buffer: STRING) -- Append object serialization data `a_serialization' into `a_buffer'. do a_buffer.append (pre_serialization_length_tag_start) if a_serialization = Void then a_buffer.append_character ('0') else a_buffer.append (a_serialization.count.out) end a_buffer.append (pre_serialization_length_tag_end) a_buffer.append_character ('%N') a_buffer.append (pre_serialization_tag_start) a_buffer.append (cdata_tag_start) if a_serialization /= Void then a_buffer.append (a_serialization) end a_buffer.append (cdata_tag_end) a_buffer.append (pre_serialization_tag_end) a_buffer.append_character ('%N') end append_object_state (a_state: detachable STRING; a_buffer: STRING; a_is_pre_state: BOOLEAN) -- Append `a_state' into `a_buffer'. do if a_is_pre_state then a_buffer.append (pre_state_tag_start) a_buffer.append_character ('%N') else a_buffer.append (post_state_tag_start) a_buffer.append_character ('%N') end if a_state /= Void then a_buffer.append (a_state) end if a_is_pre_state then a_buffer.append (pre_state_tag_end) a_buffer.append_character ('%N') else a_buffer.append (post_state_tag_end) a_buffer.append_character ('%N') end end append_variable_with_type (a_index: INTEGER; a_type: STRING; a_buffer: STRING; a_indent_needed: BOOLEAN) -- Append variable with `a_index' and `a_type' into `a_buffer'. do if a_indent_needed then a_buffer.append_character ('%T') end a_buffer.append (once "v_") a_buffer.append_integer (a_index) a_buffer.append (once ": ") if a_index = 0 then a_buffer.append (none_type_name) else a_buffer.append (a_type) end a_buffer.append_character ('%N') end invariant interpreter_attached: interpreter /= Void note copyright: "Copyright (c) 1984-2015, 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