/// <summary> /// Checks whether a given method is an observer or not /// </summary> /// <param name="m"></param> /// <returns></returns> public bool IsAnObserver(Method method) { TypeEx declaringType; if (!method.TryGetDeclaringType(out declaringType)) { return(false); } MethodEffects me; if (!MethodOrFieldAnalyzer.TryComputeMethodEffects(this, declaringType, method, null, out me)) { return(false); } if (me.WrittenInstanceFields.Count == 0) { return(true); } return(false); }
/// <summary> /// Gets the set of write methods associated with a field in the given type /// </summary> /// <param name="field"></param> /// <returns></returns> public bool TryGetWriteMethods(Field field, TypeEx declaringType, out SafeSet <Method> writeMethods) { SafeDebug.AssumeNotNull(field, "field"); SafeDebug.AssumeNotNull(declaringType, "declaringType"); //Declaring type should include defintinion if (declaringType == null || declaringType.Definition == null) { this.Log.LogWarning(WikiTopics.MissingWikiTopic, "WriteMethods", "Missing definition for the declaring type " + declaringType.FullName); writeMethods = null; return(false); } FieldStore fs; if (this.FieldDictionary.TryGetValue(field, out fs)) { if (fs.WriteMethods.TryGetValue(declaringType, out writeMethods)) { return(true); } else { writeMethods = new SafeSet <Method>(); fs.WriteMethods[declaringType] = writeMethods; } } else { fs = new FieldStore(); fs.FieldName = field; writeMethods = new SafeSet <Method>(); fs.WriteMethods[declaringType] = writeMethods; this.FieldDictionary[field] = fs; } foreach (MethodDefinition instanceMethod in declaringType.Definition.DeclaredInstanceMethods) { //Ignore abstract methods if (instanceMethod.IsAbstract) { continue; } //If the DISABLE_CALLING_METHODS_WITHIN_CLASS is set to true, then private methods need not be considered //since they will be filtered out. if (PexMeConstants.DISABLE_CALLING_METHODS_WITHIN_CLASS && instanceMethod.DeclaredVisibility == Visibility.Private) { continue; } Method method = instanceMethod.Instantiate(declaringType.GenericTypeArguments, MethodOrFieldAnalyzer.GetGenericMethodParameters(this, instanceMethod)); MethodEffects me; if (!MethodOrFieldAnalyzer.TryComputeMethodEffects(this, declaringType, method, null, out me)) { this.Log.LogWarning(WikiTopics.MissingWikiTopic, "methodeffects", "Failed to get the method effects for method " + method); continue; } if (me.WrittenInstanceFields.Contains(field.ShortName)) { writeMethods.Add(method); } FieldModificationType fmt; if (me.ModificationTypeDictionary.TryGetValue(field.ShortName, out fmt)) { fs.ModificationTypeDictionary[method] = fmt; } } //Check whether there are any private methods in calling methods. //If yes, replace them with their callers. if (!PexMeConstants.DISABLE_CALLING_METHODS_WITHIN_CLASS) { var newWriteMethods = new SafeSet <Method>(); GetCallingMethods(field, declaringType, writeMethods, newWriteMethods); writeMethods.Clear(); writeMethods.AddRange(newWriteMethods); } TypeEx baseType = declaringType.BaseType; if (baseType != null && baseType.FullName != "System.Object") { SafeSet <Method> innerWriteMethods; this.TryGetWriteMethods(field, baseType, out innerWriteMethods); if (innerWriteMethods != null) { writeMethods.AddRange(innerWriteMethods); } } return(true); }
/// <summary> /// Gets calling methods of a given called method on the given field in a given target type /// </summary> /// <returns></returns> public bool TryGetCallingMethodsInType(Method calledMethod, Field field, TypeEx targetType, out SafeSet <Method> callingMethods) { SafeDebug.AssumeNotNull(calledMethod, "method"); SafeDebug.AssumeNotNull(targetType, "type"); //Get the associated property of the field Property property; if (!MethodOrFieldAnalyzer.TryGetPropertyReadingField(this, targetType, field, out property)) { //TODO: error; } MethodStore mstore = null; if (this.MethodDictionary.TryGetValue(calledMethod, out mstore)) { if (mstore.CallingMethods.TryGetValue(targetType, out callingMethods)) { return(true); } } //No method store found. create a fresh one if (mstore == null) { mstore = new MethodStore(); mstore.methodName = calledMethod; this.MethodDictionary[calledMethod] = mstore; } callingMethods = new SafeSet <Method>(); mstore.CallingMethods[targetType] = callingMethods; TypeEx calledMethodType; if (!calledMethod.TryGetDeclaringType(out calledMethodType)) { this.Log.LogError(WikiTopics.MissingWikiTopic, "callingmethods", "Failed to get the declaring type for the method " + calledMethod.FullName); return(false); } //Needs to addess the array type issue over here. var targetdef = targetType.Definition; if (targetdef == null) { if (targetType.TypeKind != TypeKind.SzArrayElements) { this.Log.LogError(WikiTopics.MissingWikiTopic, "callingmethods", "The definition for the type " + targetType.FullName + " is null"); return(false); } else { targetdef = targetType.ElementType.Definition; } } //Analyze each method in the given type to identify the calling methods //of the given method foreach (var typeMethod in targetdef.DeclaredInstanceMethods) { Method minstance = typeMethod.Instantiate(targetType.GenericTypeArguments, MethodOrFieldAnalyzer.GetGenericMethodParameters(this, typeMethod)); MethodEffects meffects; if (!MethodOrFieldAnalyzer.TryComputeMethodEffects(this, targetType, minstance, null, out meffects)) { continue; } //Check for a direct comparison if (meffects.DirectCalledMethods.Contains(calledMethod)) { callingMethods.Add(minstance); } else { //Use vtable lookup for addressing the abstract issues foreach (var dcallMethod in meffects.DirectCalledMethods) { if (dcallMethod.IsConstructor) { continue; } TypeEx dcallMethodType; if (dcallMethod.TryGetDeclaringType(out dcallMethodType)) { if (dcallMethodType == calledMethodType || !dcallMethodType.IsAbstract || !dcallMethodType.IsInterface) { continue; } if (!dcallMethodType.IsAssignableTo(calledMethodType) && !calledMethodType.IsAssignableTo(dcallMethodType)) { continue; } } try { var lookupMethod = calledMethodType.VTableLookup(dcallMethod); if (lookupMethod != null && calledMethod == lookupMethod) { callingMethods.Add(minstance); break; } } catch (Exception ex) { this.Log.LogWarningFromException(ex, WikiTopics.MissingWikiTopic, "vtablelookup", "Failed to perform vtablelookup for " + dcallMethod.FullName + " in type " + calledMethodType.FullName); } } } } //Check whether there are any private methods in calling methods. //If yes, replace them with their callers. if (!PexMeConstants.DISABLE_CALLING_METHODS_WITHIN_CLASS) { var newCallingMethods = new SafeSet <Method>(); foreach (var callingM in callingMethods) { if (callingM.Definition.DeclaredVisibility != Visibility.Private || callingM.IsConstructor) { newCallingMethods.Add(callingM); continue; } //Get other calling methods within this type SafeSet <Method> localCM; if (this.TryGetCallingMethodsInType(callingM, field, targetType, out localCM)) { newCallingMethods.AddRange(localCM); } } callingMethods.Clear(); callingMethods.AddRange(newCallingMethods); } //Needs to further analyze parent types TypeEx baseType = targetType.BaseType; if (baseType != null && baseType.FullName != "System.Object") //TODO: Avoid string comparisons. needs to figure out how to do that { SafeSet <Method> baseCallingMethods; TryGetCallingMethodsInType(calledMethod, field, baseType, out baseCallingMethods); callingMethods.AddRange(baseCallingMethods); } return(true); }