public void Classes_with_multiple_compared_values_for_which_only_the_first_is_equal_are_different() { _right.StringValue = null; var comparer = new LambdaComparer<ClassWithProperties>(x => x.IntegerValue, x => x.StringValue); Assert.IsFalse(comparer.Equals(_left, _right)); }
public void Classes_with_single_non_equal_compared_value_are_different() { _right.IntegerValue = -1; var comparer = new LambdaComparer<ClassWithProperties>(x => x.IntegerValue); Assert.IsFalse(comparer.Equals(_left, _right)); }
public void Classes_with_single_compared_null_value_are_equal() { _right.StringValue = null; _left.StringValue = null; var comparer = new LambdaComparer<ClassWithProperties>(x => x.StringValue); Assert.IsTrue(comparer.Equals(_left, _right)); }
public void Equals_given_true_predicate_returns_True() { // arrange DateTime dateTime1 = DateTime.Today; DateTime dateTime2 = DateTime.Today; IEqualityComparer<DateTime> comparer = new LambdaComparer<DateTime>((x, y) => x.Equals(y)); // act bool equals = comparer.Equals(dateTime1, dateTime2); // assert Assert.IsTrue(equals); }
public void Equals_given_false_predicate_returns_False() { // arrange DateTime dateTime1 = DateTime.MinValue; DateTime dateTime2 = DateTime.MaxValue; IEqualityComparer<DateTime> comparer = new LambdaComparer<DateTime>((x, y) => x.Equals(y)); // act bool equals = comparer.Equals(dateTime1, dateTime2); // assert Assert.IsFalse(equals); }
public void Equals_given_same_reference_returns_True() { // arrange object object1 = new object(); object object2 = object1; IEqualityComparer<object> comparer = new LambdaComparer<object>((x, y) => x.Equals(y)); // act bool equals = comparer.Equals(object1, object2); // assert Assert.IsTrue(equals); }
private void ExtractBasicBlocksOfMethod(MethodReference method_reference) { MethodReference original_method_reference = method_reference; _methods_done.Add(original_method_reference.FullName); // MethodReference new_method_reference = original_method_reference.SubstituteMethod2(); // method_reference = new_method_reference != null ? new_method_reference : original_method_reference; MethodDefinition method_definition = method_reference.Resolve(); if (method_definition == null || method_definition.Body == null) { return; } int change_set = Cfg.StartChangeSet(); int instruction_count = method_definition.Body.Instructions.Count; List <INST> split_point = new List <INST>(); CFG.Vertex basic_block = (CFG.Vertex)Cfg.AddVertex(new CFG.Vertex() { Name = Cfg.NewNodeNumber().ToString() }); basic_block._method_definition = method_definition; basic_block._method_reference = original_method_reference; basic_block.Entry = basic_block; basic_block.Exit = basic_block; basic_block.Entry.BlocksOfMethod = new List <CFG.Vertex>(); basic_block.Entry.BlocksOfMethod.Add(basic_block); Cfg.Entries.Add(basic_block); // Add instructions to the basic block, including debugging information. // First, get debugging information on line/column/offset in method. if (!original_method_reference.Module.HasSymbols) { // Try to get symbols, but if none available, don't worry about it. try { original_method_reference.Module.ReadSymbols(); } catch { } } var symbol_reader = original_method_reference.Module.SymbolReader; var method_debug_information = symbol_reader?.Read(method_definition); Collection <SequencePoint> sequence_points = method_debug_information != null ? method_debug_information.SequencePoints : new Collection <SequencePoint>(); Mono.Cecil.Cil.MethodBody body = method_definition.Body; if (body == null) { throw new Exception("Body null, not expecting it to be."); } if (body.Instructions == null) { throw new Exception("Body has instructions collection."); } if (body.Instructions.Count == 0) { throw new Exception("Body instruction count is zero."); } SequencePoint previous_sp = null; for (int j = 0; j < instruction_count; ++j) { Mono.Cecil.Cil.Instruction instruction = body.Instructions[j]; SequencePoint sp = sequence_points.Where(s => { return(s.Offset == instruction.Offset); }).FirstOrDefault(); if (sp == null) { sp = previous_sp; } INST wrapped_instruction = INST.Wrap(instruction, basic_block, sp); basic_block.Instructions.Add(wrapped_instruction); if (sp != null) { previous_sp = sp; } } var instructions_before_splits = basic_block.Instructions.ToList(); // Accumulate targets of jumps. These are split points for block "v". // Accumalated splits are in "leader_list" following this for-loop. for (int j = 0; j < instruction_count; ++j) { INST wrapped_instruction = basic_block.Instructions[j]; Mono.Cecil.Cil.OpCode opcode = wrapped_instruction.OpCode; Mono.Cecil.Cil.FlowControl flow_control = opcode.FlowControl; switch (flow_control) { case Mono.Cecil.Cil.FlowControl.Branch: case Mono.Cecil.Cil.FlowControl.Cond_Branch: { object operand = wrapped_instruction.Operand; // Two cases of branches: // 1) operand is a single instruction; // 2) operand is an array of instructions via a switch instruction. // In doing this type casting, the resulting instructions are turned // into def's, and operands no longer correspond to what was in the original // method. We override the List<> compare to correct this problem. Mono.Cecil.Cil.Instruction single_instruction = operand as Mono.Cecil.Cil.Instruction; Mono.Cecil.Cil.Instruction[] array_of_instructions = operand as Mono.Cecil.Cil.Instruction[]; if (single_instruction != null) { INST sp = null; for (int i = 0; i < basic_block.Instructions.Count(); ++i) { if (basic_block.Instructions[i].Offset == single_instruction.Offset && basic_block.Instructions[i].OpCode == single_instruction.OpCode) { sp = basic_block.Instructions[i]; break; } } if (sp == null) { throw new Exception("Instruction go to not found."); } bool found = false; foreach (INST split in split_point) { if (split.Offset == sp.Offset && split.OpCode == sp.OpCode) { found = true; break; } } if (!found) { split_point.Add(sp); } } else if (array_of_instructions != null) { foreach (var ins in array_of_instructions) { Debug.Assert(ins != null); INST sp = null; for (int i = 0; i < basic_block.Instructions.Count(); ++i) { if (basic_block.Instructions[i].Offset == ins.Offset && basic_block.Instructions[i].OpCode == ins.OpCode) { sp = basic_block.Instructions[i]; break; } } if (sp == null) { throw new Exception("Instruction go to not found."); } bool found = false; foreach (INST split in split_point) { if (split.Offset == sp.Offset && split.OpCode == sp.OpCode) { found = true; break; } } if (!found) { split_point.Add(sp); } } } else { throw new Exception("Unknown operand type for basic block partitioning."); } } break; } // Split blocks after certain instructions, too. switch (flow_control) { case Mono.Cecil.Cil.FlowControl.Branch: case Mono.Cecil.Cil.FlowControl.Call: case Mono.Cecil.Cil.FlowControl.Cond_Branch: case Mono.Cecil.Cil.FlowControl.Return: case Mono.Cecil.Cil.FlowControl.Throw: { if (flow_control == Mono.Cecil.Cil.FlowControl.Call && !Campy.Utils.Options.IsOn("split_at_calls")) { break; } if (j + 1 < instruction_count) { var ins = basic_block.Instructions[j + 1].Instruction; INST sp = null; for (int i = 0; i < basic_block.Instructions.Count(); ++i) { if (basic_block.Instructions[i].Offset == ins.Offset && basic_block.Instructions[i].OpCode == ins.OpCode) { sp = basic_block.Instructions[i]; break; } } if (sp == null) { throw new Exception("Instruction go to not found."); } bool found = false; foreach (INST split in split_point) { if (split.Offset == sp.Offset && split.OpCode == sp.OpCode) { found = true; break; } } if (!found) { split_point.Add(sp); } } } break; } } // Get try-catch blocks and add those split points. foreach (var eh in body.ExceptionHandlers) { var start = eh.TryStart; var end = eh.TryEnd; // Split at try start. Instruction ins = start; INST sp = null; for (int i = 0; i < basic_block.Instructions.Count(); ++i) { if (basic_block.Instructions[i].Offset == ins.Offset && basic_block.Instructions[i].OpCode == ins.OpCode) { sp = basic_block.Instructions[i]; break; } } if (sp == null) { throw new Exception("Instruction go to not found."); } bool found = false; foreach (INST split in split_point) { if (split.Offset == sp.Offset && split.OpCode == sp.OpCode) { found = true; break; } } if (!found) { split_point.Add(sp); } } // Note, we assume that these splits are within the same method. // Order the list according to offset from beginning of the method. List <INST> ordered_leader_list = new List <INST>(); for (int j = 0; j < instruction_count; ++j) { // Order jump targets. These denote locations // where to split blocks. However, it's ordered, // so that splitting is done from last instruction in block // to first instruction in block. INST i = basic_block.Instructions[j]; if (split_point.Contains(i, new LambdaComparer <INST>( (INST a, INST b) => { if (a.Offset != b.Offset) { return(false); } if (a.OpCode != b.OpCode) { return(false); } return(true); }))) { ordered_leader_list.Add(i); } } if (ordered_leader_list.Count != split_point.Count) { throw new Exception( "Mono Cecil giving weird results for instruction operand type casting. Size of original split points not the same as order list of split points."); } // Split block at all jump targets. foreach (INST i in ordered_leader_list) { var owner = Cfg.PeekChangeSet(change_set).Where( n => n.Instructions.Where(ins => { if (ins.Offset != i.Offset) { return(false); } if (ins.OpCode != i.OpCode) { return(false); } return(true); } ).Any()).ToList(); // Check if there are multiple nodes with the same instruction or if there isn't // any node found containing the instruction. Either way, it's a programming error. if (owner.Count != 1) { throw new Exception("Cannot find instruction!"); } CFG.Vertex target_node = owner.FirstOrDefault(); var j = target_node.Instructions.FindIndex(a => { if (a.Offset != i.Offset) { return(false); } if (a.OpCode != i.OpCode) { return(false); } return(true); }); CFG.Vertex new_node = Split(target_node, j); } LambdaComparer <Instruction> fixed_comparer = new LambdaComparer <Instruction>( (Instruction a, Instruction b) => a.Offset == b.Offset && a.OpCode == b.OpCode ); // Add in all edges. var list_new_nodes = Cfg.PopChangeSet(change_set); foreach (var node in list_new_nodes) { int node_instruction_count = node.Instructions.Count; INST last_instruction = node.Instructions[node_instruction_count - 1]; Mono.Cecil.Cil.OpCode opcode = last_instruction.OpCode; Mono.Cecil.Cil.FlowControl flow_control = opcode.FlowControl; // Add jump edge. switch (flow_control) { case Mono.Cecil.Cil.FlowControl.Branch: case Mono.Cecil.Cil.FlowControl.Cond_Branch: { // Two cases: i.Operand is a single instruction, or an array of instructions. if (last_instruction.Operand as Mono.Cecil.Cil.Instruction != null) { // Handel leave instructions with code below. if (!(last_instruction.OpCode.Code == Code.Leave || last_instruction.OpCode.Code == Code.Leave_S)) { Mono.Cecil.Cil.Instruction target_instruction = last_instruction.Operand as Mono.Cecil.Cil.Instruction; CFG.Vertex target_node = list_new_nodes.FirstOrDefault( (CFG.Vertex x) => { if (!fixed_comparer.Equals(x.Instructions.First().Instruction, target_instruction)) { return(false); } return(true); }); if (target_node != null) { Cfg.AddEdge(new CFG.Edge() { From = node, To = target_node }); } } } else if (last_instruction.Operand as Mono.Cecil.Cil.Instruction[] != null) { foreach (Mono.Cecil.Cil.Instruction target_instruction in (last_instruction.Operand as Mono.Cecil.Cil.Instruction[])) { CFG.Vertex target_node = list_new_nodes.FirstOrDefault( (CFG.Vertex x) => { if (!fixed_comparer.Equals(x.Instructions.First().Instruction, target_instruction)) { return(false); } return(true); }); if (target_node != null) { Cfg.AddEdge(new CFG.Edge() { From = node, To = target_node }); } } } else { throw new Exception("Unknown operand type for conditional branch."); } break; } } // Add fall through edge. switch (flow_control) { //case Mono.Cecil.Cil.FlowControl.Branch: //case Mono.Cecil.Cil.FlowControl.Break: case Mono.Cecil.Cil.FlowControl.Call: case Mono.Cecil.Cil.FlowControl.Cond_Branch: case Mono.Cecil.Cil.FlowControl.Meta: case Mono.Cecil.Cil.FlowControl.Next: case Mono.Cecil.Cil.FlowControl.Phi: case Mono.Cecil.Cil.FlowControl.Throw: { int next = instructions_before_splits.FindIndex( n => { var r = n == last_instruction; return(r); } ); if (next < 0) { break; } next += 1; if (next >= instructions_before_splits.Count) { break; } var next_instruction = instructions_before_splits[next]; var owner = next_instruction.Block; CFG.Vertex target_node = owner; Cfg.AddEdge(new CFG.Edge() { From = node, To = target_node }); } break; case Mono.Cecil.Cil.FlowControl.Return: if (last_instruction.Instruction.OpCode.Code == Code.Endfinally) { // Although the exception handling is like a procedure call, // local variables are all accessible. So, we need to copy stack // values around. In addition, we have to create a fall through // even though there is stack unwinding. int next = instructions_before_splits.FindIndex( n => { var r = n == last_instruction; return(r); } ); if (next < 0) { break; } next += 1; if (next >= instructions_before_splits.Count) { break; } var next_instruction = instructions_before_splits[next]; var owner = next_instruction.Block; CFG.Vertex target_node = owner; Cfg.AddEdge(new CFG.Edge() { From = node, To = target_node }); } break; } } // Get inclusive start/exclusive end ranges of try/catch/finally. Dictionary <int, int> exclusive_eh_range = new Dictionary <int, int>(); foreach (var eh in body.ExceptionHandlers) { int try_start = eh.TryStart.Offset; int eh_end = eh.TryEnd != null ? eh.TryEnd.Offset : 0; if (eh.TryEnd != null && eh.TryEnd.Offset > eh_end) { eh_end = eh.TryEnd.Offset; } if (eh.HandlerEnd != null && eh.HandlerEnd.Offset > eh_end) { eh_end = eh.HandlerEnd.Offset; } exclusive_eh_range[try_start] = eh_end; } // Get inclusive start/inclusive end ranges of try/catch/finally. Dictionary <int, int> inclusive_eh_range = new Dictionary <int, int>(); foreach (var pair in exclusive_eh_range) { int previous_instruction_address = 0; foreach (var i in body.Instructions) { if (pair.Value == i.Offset) { inclusive_eh_range[pair.Key] = previous_instruction_address; break; } previous_instruction_address = i.Offset; } } // Get "finally" blocks for each try, if there is one. Dictionary <int, CFG.Vertex> try_finally_block = new Dictionary <int, CFG.Vertex>(); foreach (var eh in body.ExceptionHandlers) { if (eh.HandlerType == ExceptionHandlerType.Finally) { var finally_entry_block = list_new_nodes.Where( n => { var first = n.Instructions.First().Instruction; if (first.Offset == eh.HandlerStart.Offset) { return(true); } else { return(false); } }).First(); try_finally_block[eh.TryStart.Offset] = finally_entry_block; } } // Set block properties. foreach (var eh in body.ExceptionHandlers) { var block = list_new_nodes.Where( n => { var first = n.Instructions.First().Instruction; if (first.Offset == eh.HandlerStart.Offset) { return(true); } else { return(false); } }).First(); block.CatchType = eh.CatchType; block.IsCatch = eh.HandlerType == ExceptionHandlerType.Catch; block.ExceptionHandler = eh; } // Get "try" block for each try. Dictionary <int, CFG.Vertex> try_entry_block = new Dictionary <int, CFG.Vertex>(); foreach (var pair in inclusive_eh_range) { int start = pair.Key; var entry_block = list_new_nodes.Where( n => { var first = n.Instructions.First().Instruction; if (first.Offset == start) { return(true); } else { return(false); } }).First(); try_entry_block[start] = entry_block; } // Get entry block for each exception handler. Dictionary <int, CFG.Vertex> eh_entry_block = new Dictionary <int, CFG.Vertex>(); foreach (var eh in body.ExceptionHandlers) { int start = eh.HandlerStart.Offset; var entry_block = list_new_nodes.Where( n => { var first = n.Instructions.First().Instruction; if (first.Offset == start) { return(true); } else { return(false); } }).First(); eh_entry_block[start] = entry_block; } foreach (var eh in body.ExceptionHandlers) { int start = eh.TryStart.Offset; var try_block = try_entry_block[start]; int eh_start = eh.HandlerStart.Offset; var eh_block = eh_entry_block[eh_start]; if (eh.HandlerType == ExceptionHandlerType.Finally) { continue; } foreach (var prev in list_new_nodes.First()._graph.Predecessors(try_block)) { Cfg.AddEdge(new CFG.Edge() { From = prev, To = eh_block }); } } // Go through all CIL "leave" instructions and draw up edges. Any leave to end of // endfinally block requires edge to finally block, not the following instruction. foreach (var node in list_new_nodes) { int node_instruction_count = node.Instructions.Count; INST leave_instruction = node.Instructions[node_instruction_count - 1]; Mono.Cecil.Cil.OpCode opcode = leave_instruction.OpCode; Mono.Cecil.Cil.FlowControl flow_control = opcode.FlowControl; if (!(leave_instruction.OpCode.Code == Code.Leave || leave_instruction.OpCode.Code == Code.Leave_S)) { continue; } // Link up any leave instructions object operand = leave_instruction.Operand; Mono.Cecil.Cil.Instruction single_instruction = operand as Mono.Cecil.Cil.Instruction; Mono.Cecil.Cil.Instruction[] array_of_instructions = operand as Mono.Cecil.Cil.Instruction[]; if (single_instruction == null) { throw new Exception("Malformed leave instruction."); } KeyValuePair <int, int> pair = inclusive_eh_range.Where(p => p.Key <= leave_instruction.Instruction.Offset && leave_instruction.Instruction.Offset <= p.Value).FirstOrDefault(); // pair indicates what try/catch/finally block. If not in a try/catch/finally, // draw edge to destination. If the destination is outside try/catch/finally, // draw edge to destination. if (pair.Value == 0 || single_instruction.Offset >= pair.Key && single_instruction.Offset <= pair.Value) { var whereever = list_new_nodes.Where( n => { var first = n.Instructions.First().Instruction; if (first.Offset == single_instruction.Offset) { return(true); } else { return(false); } }).First(); Cfg.AddEdge(new CFG.Edge() { From = node, To = whereever }); continue; } if (try_finally_block.ContainsKey(pair.Key)) { Cfg.AddEdge(new CFG.Edge() { From = node, To = try_finally_block[pair.Key] }); } else { var whereever = list_new_nodes.Where( n => { var first = n.Instructions.First().Instruction; if (first.Offset == single_instruction.Offset) { return(true); } else { return(false); } }).First(); Cfg.AddEdge(new CFG.Edge() { From = node, To = whereever }); } } if (Campy.Utils.Options.IsOn("detailed_import_computation_trace")) { Cfg.OutputDotGraph(); } if (Campy.Utils.Options.IsOn("detailed_import_computation_trace")) { Cfg.OutputEntireGraph(); } }
public void Computed_values_can_be_used_for_comparison() { _left.StringValue = "ABC"; _right.StringValue = "abc"; _left.IntegerValue = 1; _right.IntegerValue = 2; var comparer = new LambdaComparer<ClassWithProperties>( x => x.IntegerValue > 0, x => x.StringValue.ToLower(), x => x.StringValue.ToUpper() + x.IntegerValue / x.IntegerValue, x => true); Assert.IsTrue(comparer.Equals(_left, _right)); }
public void Classes_with_multiple_equal_compared_valeus_are_equal() { var comparer = new LambdaComparer<ClassWithProperties>(x => x.IntegerValue, x => x.StringValue); Assert.IsTrue(comparer.Equals(_left, _right)); }
public void Exception_is_thrown_if_no_value_getters_are_specified() { var comparer = new LambdaComparer<ClassWithProperties>(); comparer.Equals(_left, _right); }
public void AreEqual(Expression left, Expression right) { var exc = new LambdaComparer(); Assert.True(exc.Equals(left, right)); }