public static MethodContract /*?*/ ExtractContracts(IContractAwareHost contractAwareHost, PdbReader /*?*/ pdbReader, ContractExtractor extractor, ISourceMethodBody methodBody) { var definingUnit = TypeHelper.GetDefiningUnit(methodBody.MethodDefinition.ContainingType.ResolvedType); var methodIsInReferenceAssembly = ContractHelper.IsContractReferenceAssembly(contractAwareHost, definingUnit); var oldAndResultExtractor = new OldAndResultExtractor(contractAwareHost, methodBody, extractor.IsContractMethod); var localsInitializedWithFields = FindLocals.FindSetOfLocals(methodBody); var har = new HermansAlwaysRight(contractAwareHost, extractor, methodBody, methodIsInReferenceAssembly, oldAndResultExtractor, pdbReader); har.Rewrite(methodBody); if (har.extractor.currentMethodContract == null) { return(null); } // The decompiler will have introduced locals if there were any anonymous delegates in the contracts // Such locals are initialized with the fields of the iterator class. // The contract that comes back from here will have those fields replaced with whatever the iterator captured // (parameters, locals). So the locals in the contract need to be replaced with the iterator fields so that // next replacement will see the right thing (i.e., the fields) and replace them! Phew! var localReplacer = new LocalReplacer(contractAwareHost, localsInitializedWithFields); localReplacer.Rewrite(har.extractor.currentMethodContract); // also need to rewrite the remainder of the body localReplacer.Rewrite(methodBody); return(har.extractor.currentMethodContract); }
// TODO: First search the moveNextBody to see if there are any contracts at all. /// <summary> /// /// </summary> /// <param name="host"></param> /// <param name="extractor"></param> /// <param name="iteratorMethodBody"></param> /// <param name="moveNextBody"></param> /// <param name="pdbReader"></param> /// <returns></returns> public static MethodContract GetMethodContractFromMoveNext( IContractAwareHost host, ContractExtractor extractor, ISourceMethodBody iteratorMethodBody, ISourceMethodBody moveNextBody, PdbReader pdbReader ) { // Walk the iterator method and collect all of the state that is assigned to fields in the iterator class // That state needs to replace any occurrences of the fields in the contracts (if they exist...) var iteratorStmts = new List <IStatement>(iteratorMethodBody.Block.Statements); Dictionary <uint, IExpression> capturedThings = new Dictionary <uint, IExpression>(); // Find all of the state captured for the IEnumerable // REVIEW: Is this state ever used in the contracts? Since they're all sitting in the MoveNext // method, maybe they always use the IEnumerator state? if (1 < iteratorStmts.Count) { // First statement should be the creation of the iterator class int j = 1; while (j < iteratorStmts.Count) { var es = iteratorStmts[j++] as IExpressionStatement; if (es == null) { break; } var assign = es.Expression as IAssignment; if (assign == null) { break; } var field = assign.Target.Definition as IFieldReference; var capturedThing = assign.Source; var k = field.InternedKey; var spec = field as ISpecializedFieldReference; if (spec != null) { k = spec.UnspecializedVersion.InternedKey; } capturedThings.Add(k, capturedThing); } } else { var ret = iteratorStmts[0] as IReturnStatement; if (ret != null) { var be = ret.Expression as IBlockExpression; if (be != null) { var beStmts = new List <IStatement>(be.BlockStatement.Statements); var j = 1; while (j < beStmts.Count) { var es = beStmts[j++] as IExpressionStatement; if (es == null) { break; } var assign = es.Expression as IAssignment; if (assign == null) { break; } var field = assign.Target.Definition as IFieldReference; var capturedThing = assign.Source; var k = field.InternedKey; var spec = field as ISpecializedFieldReference; if (spec != null) { k = spec.UnspecializedVersion.InternedKey; } capturedThings.Add(k, capturedThing); } } } } // Find all of the state captured for the IEnumerator // That state is captured at the beginning of the IEnumerable<T>.GetEnumerator method IMethodDefinition getEnumerator = null; var t = moveNextBody.MethodDefinition.ContainingTypeDefinition; foreach (IMethodImplementation methodImplementation in t.ExplicitImplementationOverrides) { if (methodImplementation.ImplementedMethod.Name == host.NameTable.GetNameFor("GetEnumerator")) { var gtir = methodImplementation.ImplementedMethod.ContainingType as IGenericTypeInstanceReference; if (gtir != null && TypeHelper.TypesAreEquivalent(gtir.GenericType, host.PlatformType.SystemCollectionsGenericIEnumerable)) { getEnumerator = methodImplementation.ImplementingMethod.ResolvedMethod as IMethodDefinition; break; } } } if (getEnumerator != null) { IMethodBody geBody = getEnumerator.Body; var sourceGeBody = geBody as ISourceMethodBody; if (sourceGeBody == null) { sourceGeBody = Decompiler.GetCodeModelFromMetadataModel(host, geBody, pdbReader, DecompilerOptions.AnonymousDelegates); } foreach (var stmt in sourceGeBody.Block.Statements) { var es = stmt as IExpressionStatement; if (es == null) { continue; } var assign = es.Expression as IAssignment; if (assign == null) { continue; } var field2 = assign.Target.Definition as IFieldReference; if (field2 == null) { continue; } var k = field2.InternedKey; var spec = field2 as ISpecializedFieldReference; if (spec != null) { k = spec.UnspecializedVersion.InternedKey; } var sourceBe = assign.Source as IBoundExpression; if (sourceBe == null) { continue; } var field3 = sourceBe.Definition as IFieldReference; if (field3 == null) { continue; } var k3 = field3.InternedKey; var spec3 = field3 as ISpecializedFieldReference; if (spec3 != null) { k3 = spec3.UnspecializedVersion.InternedKey; } IExpression capturedThing = null; if (!capturedThings.TryGetValue(k3, out capturedThing)) { continue; } capturedThings[k] = capturedThing; } } var mc = HermansAlwaysRight.ExtractContracts(host, pdbReader, extractor, moveNextBody); if (mc == null) { return(mc); } // substitute all field references in contract with the captured state var replacer = new Replacer(host, capturedThings, iteratorMethodBody.MethodDefinition, moveNextBody.MethodDefinition); replacer.RewriteChildren(mc); if (moveNextBody.MethodDefinition.ContainingTypeDefinition.IsGeneric) { var genericParameterMapper = new GenericMethodParameterMapper(host, iteratorMethodBody.MethodDefinition, moveNextBody.MethodDefinition.ContainingType as INestedTypeReference); mc = genericParameterMapper.Rewrite(mc) as MethodContract; } return(mc); }
public static MethodContract/*?*/ ExtractContracts(IContractAwareHost contractAwareHost, PdbReader/*?*/ pdbReader, ContractExtractor extractor, ISourceMethodBody methodBody) { var definingUnit = TypeHelper.GetDefiningUnit(methodBody.MethodDefinition.ContainingType.ResolvedType); var methodIsInReferenceAssembly = ContractHelper.IsContractReferenceAssembly(contractAwareHost, definingUnit); var oldAndResultExtractor = new OldAndResultExtractor(contractAwareHost, methodBody, extractor.IsContractMethod); var localsInitializedWithFields = FindLocals.FindSetOfLocals(methodBody); var har = new HermansAlwaysRight(contractAwareHost, extractor, methodBody, methodIsInReferenceAssembly, oldAndResultExtractor, pdbReader); har.Rewrite(methodBody); if (har.extractor.currentMethodContract == null) return null; // The decompiler will have introduced locals if there were any anonymous delegates in the contracts // Such locals are initialized with the fields of the iterator class. // The contract that comes back from here will have those fields replaced with whatever the iterator captured // (parameters, locals). So the locals in the contract need to be replaced with the iterator fields so that // next replacement will see the right thing (i.e., the fields) and replace them! Phew! var localReplacer = new LocalReplacer(contractAwareHost, localsInitializedWithFields); localReplacer.Rewrite(har.extractor.currentMethodContract); // also need to rewrite the remainder of the body localReplacer.Rewrite(methodBody); return har.extractor.currentMethodContract; }