/// <summary> /// Call-back that gets invoked when Pex requires a factory method /// for a specific type. /// TODO: May not be invoked when there is some existing factory method /// already there and the new uncovered branch is due to object creation issue /// Or will be invoked only if Pex thinks that it needs a factory method. /// </summary> /// <param name="explorableType"></param> /// <returns></returns> public IEnumerable <PexExplorableCandidate> GuessExplorables(TypeEx explorableType) { SafeDebug.AssumeNotNull(explorableType, "explorableType"); this.host.Log.LogMessage(PexMeLogCategories.MethodBegin, "Beginning of PexMeFactoryGuesser.GuessExplorables method"); this.host.Log.LogMessage(PexMeLogCategories.Debug, "Requested for type: " + explorableType); //A trick to make generics work properly with the combination of inheritance. //Check the class PreDefinedGenericClasses for more details of why this line of code is required PreDefinedGenericClasses.recentAccessedTypes.Add(explorableType.FullName); var visibilityContext = VisibilityContext.Exported; if (!explorableType.IsVisible(visibilityContext))//if the type is not public { yield break; } //Giving mseqgen factories the highest preferemce if (PexMeConstants.ENABLE_MSEQGEN_RECOMMENDER) { if (mseqgen == null) { mseqgen = this.pmd.GetService <MSeqGenRecommender>(); } foreach (var factory in mseqgen.GetMSeqGenFactories(explorableType)) { yield return(factory); } } //the following factory is not returned to be used in sequence explroation but used to check //whether things are valid PexExplorableFactory localExplorableFactory; bool result = PexExplorableFactory.TryGetExplorableFactory(this.host, explorableType, out localExplorableFactory); Method bestConstructorMethod = null; if (explorableType.DefaultConstructor == null) { #region scan visible constructors, order by call depth, select best constructor var orderedMethodEffectsList = new SafeList <OrderedMethodEffects>(); var bestConstructor = new OrderedMethodEffects(); bool bNoVisibleConstructors = true; foreach (var constructor in explorableType.GetVisibleInstanceConstructors(visibilityContext)) { if (!localExplorableFactory.IsValidFactoryMethod(constructor)) { continue; } orderedMethodEffectsList.Add(new OrderedMethodEffects(this.host, constructor)); bNoVisibleConstructors = false; } if (!bNoVisibleConstructors) { //Finding the default constructor. We always start with the default constructor orderedMethodEffectsList.Sort(); foreach (var entry in orderedMethodEffectsList) { if (bestConstructor.Method == null || bestConstructor.Effects.WrittenInstanceFields.Count > entry.Effects.WrittenInstanceFields.Count) { bestConstructor = entry; } } orderedMethodEffectsList.Clear(); if (bestConstructor.Method != null) //cannot find a constructor { bestConstructorMethod = bestConstructor.Method; } } if (bestConstructorMethod == null) { if (!TypeAnalyzer.TryGetProducingMethods(this.pmd, explorableType, out bestConstructorMethod)) { yield break; } } #endregion } else { bestConstructorMethod = explorableType.DefaultConstructor; } #region default factory method from the original Pex: scan visible methods, order by call depth, add methods as setters //start building the method sequence PexExplorableFactory originalExplorableFactory; result = PexExplorableFactory.TryGetExplorableFactory(this.Host, explorableType, out originalExplorableFactory); //add constructor if (!originalExplorableFactory.TrySetFactoryMethod(bestConstructorMethod)) { SafeDebug.Fail("we checked before that it is valid"); yield break; } IPexExplorable originalExplorable1 = originalExplorableFactory.CreateExplorable(); CodeUpdate.AddMethodCodeUpdate originalPreviewUpdate1; CodeUpdate originalUpdate1 = originalExplorableFactory.CreateExplorableFactoryUpdate(out originalPreviewUpdate1); //return the original one after own suggested this.WriteOutMethodBody(explorableType, originalPreviewUpdate1); yield return(new PexExplorableCandidate(originalExplorable1, false, originalUpdate1)); var fsuggestions = this.pmd.FactorySuggestionsDictionary; //No suggestions for this type are available FactorySuggestionStore fss; if (fsuggestions.Count != 0 && fsuggestions.TryGetValue(explorableType.ToString(), out fss)) { var methodNameToMethodMapper = new SafeDictionary <string, Method>(); var propertyNameToPropertyMapper = new SafeDictionary <string, Property>(); //Trying to add the remaining method setters ExtractMethodsAndProperties(explorableType, methodNameToMethodMapper, propertyNameToPropertyMapper); //PexMe suggested factory methods foreach (var msequence in fss.GetSuggestedMethodSequences(this.pmd)) { PexExplorableFactory pexmeExplorableFactory; result = PexExplorableFactory.TryGetExplorableFactory(this.host, explorableType, out pexmeExplorableFactory); bool bRecommendThisFactoryMethod = false; try { //Check whether the sequence includes a constructor //If yes use the constructor Method bestConstructorMethodSuggested = null; foreach (var methodid in msequence.Sequence) { if (!methodid.Contains("..ctor(")) { continue; } Method tempMethod; if (methodNameToMethodMapper.TryGetValue(methodid, out tempMethod)) { bestConstructorMethodSuggested = tempMethod; } else { this.host.Log.LogWarning(WikiTopics.MissingWikiTopic, "factoryguesser", "Failed to the get the method with ID: " + methodid); } } if (bestConstructorMethodSuggested == null) { if (!pexmeExplorableFactory.TrySetFactoryMethod(bestConstructorMethod)) { SafeDebug.Fail("we checked before that it is valid"); yield break; } } else { if (!pexmeExplorableFactory.TrySetFactoryMethod(bestConstructorMethodSuggested)) { this.host.Log.LogWarning(WikiTopics.MissingWikiTopic, "factoryguesser", "Failed to set best suggested constructor method " + bestConstructorMethodSuggested.FullName); yield break; } else { bRecommendThisFactoryMethod = true; } } //handle other methods foreach (var methodid in msequence.Sequence) { if (methodid.Contains("..ctor(")) { continue; } //Could be a setter method for the property if (methodid.Contains("set_")) { Property prop; if (propertyNameToPropertyMapper.TryGetValue(methodid, out prop)) { if (!pexmeExplorableFactory.TryAddPropertySetter(prop)) { this.host.Log.LogWarning(WikiTopics.MissingWikiTopic, "factoryguesser", "Failed to add property " + prop.FullName + " to the factory method"); } else { bRecommendThisFactoryMethod = true; } continue; } } Method smethod; if (methodNameToMethodMapper.TryGetValue(methodid, out smethod)) { if (!pexmeExplorableFactory.TryAddMethodSetter(smethod)) { this.host.Log.LogWarning(WikiTopics.MissingWikiTopic, "factoryguesser", "Failed to add method " + smethod.FullName + " to the factory method"); } else { bRecommendThisFactoryMethod = true; } } } } catch (System.Exception ex) { this.host.Log.LogError(WikiTopics.MissingWikiTopic, "ExplorableGuesser", " Exception occurred while constructing factory from suggested sequence " + ex.Message); continue; } //no method are being added to this sequence. This can be of no use. if (!bRecommendThisFactoryMethod) { continue; } IPexExplorable originalExplorable = pexmeExplorableFactory.CreateExplorable(); CodeUpdate.AddMethodCodeUpdate originalPreviewUpdate; CodeUpdate originalUpdate = pexmeExplorableFactory.CreateExplorableFactoryUpdate(out originalPreviewUpdate); this.WriteOutMethodBody(explorableType, originalPreviewUpdate); yield return(new PexExplorableCandidate(originalExplorable, false, originalUpdate)); } } #endregion yield break; }
/// <summary> /// scans the assembly and identifies all MSeqGen generated factory methods /// </summary> /// <returns></returns> public void LoadExplorableCandidates() { var pmd = this.GetService <PexMeDynamicDatabase>(); var fss = pmd.FactorySuggestionsDictionary; foreach (var tdef in this.currAssembly.TypeDefinitions) { if (!tdef.ShortName.EndsWith(MSeqGenConstants.FACTORY_CLASS_SUFFIX)) { continue; } foreach (var mdef in tdef.DeclaredStaticMethods) { PexExplorableFactory originalExplorableFactory = null; TypeEx retTypeEx = null; try { var retType = mdef.ResultType; if (!MethodOrFieldAnalyzer.TryGetTypeExFromName(this, this.currAssembly, retType.ToString(), out retTypeEx)) { this.Log.LogWarning(WikiTopics.MissingWikiTopic, "MSeqGenRecommender", "Failed to set typeex for " + retType.ToString()); continue; } //var retTypeEx = MetadataFromReflection.GetType(retType.GetType()); var methodEx = mdef.Instantiate(MethodOrFieldAnalyzer.GetGenericTypeParameters(this, retTypeEx.Definition), MethodOrFieldAnalyzer.GetGenericMethodParameters(this, mdef)); var result = PexExplorableFactory.TryGetExplorableFactory(this, retTypeEx, out originalExplorableFactory); if (result == false) { this.Log.LogWarning(WikiTopics.MissingWikiTopic, "MSeqGenRecommender", "Failed to set create explorable for " + retTypeEx.FullName); continue; } //add constructor if (!originalExplorableFactory.TrySetFactoryMethod(methodEx)) { this.Log.LogWarning(WikiTopics.MissingWikiTopic, "MSeqGenRecommender", "Failed to set factory method for " + mdef.FullName); continue; } } catch (Exception ex) { this.Log.LogError(WikiTopics.MissingWikiTopic, "MSeqGenRecommender", "Error occurred while parsing MSeqGen factories " + ex.Message); } if (originalExplorableFactory == null) { continue; } IPexExplorable originalExplorable1 = originalExplorableFactory.CreateExplorable(); CodeUpdate.AddMethodCodeUpdate originalPreviewUpdate1; CodeUpdate originalUpdate1 = originalExplorableFactory.CreateExplorableFactoryUpdate(out originalPreviewUpdate1); this.AddToRecommendedFactories(retTypeEx.FullName, new PexExplorableCandidate(originalExplorable1, false, originalUpdate1)); } } }