using System;
using System.Reflection;
using System.Xml;
using System.Collections;
using System.Collections.Specialized;
using AnticipatingMinds.Genesis.CodeDOM;
using AnticipatingMinds.CommonUIControls;
using AnticipatingMinds.Genesis.CodeDOM.Utilities;
using AnticipatingMinds.Genesis.KnowledgeManagement;
using AnticipatingMinds.Genesis.Effectors;
namespace AnticipatingMinds.KnowledgePack.Threading{
public class ProtectLockReleaseRuleTemplate : RuleTemplate
{
public ProtectLockReleaseRuleTemplate(string templateId, IDictionary templateProperties):base(templateId,templateProperties)
{
KnowledgePackLicense.Validate();
}
public override string Name
{
get
{
return ResourceManager.GetLocalizedString("ProtectLockReleaseRuleTemplate|Name");
}
}
public override string Description
{
get
{
return ResourceManager.GetLocalizedString("ProtectLockReleaseRuleTemplate|Description");
}
}
public override Rule CreateRule(string ruleId, IDictionary ruleProperties)
{
return new ProtectLockReleaseRule(ruleId,Id,ruleProperties);
}
}
public class ProtectLockReleaseRule : Rule
{
internal ProtectLockReleaseRule(string id, string templateId, IDictionary ruleProperties) : base(id,templateId,ruleProperties)
{
}
class LockData
{
public string lockSignature;
public CodeExpression lockExpression;
public CodeMethodInvokeExpression lockEnterance = null;
public CodeMethodInvokeExpression lastLockExit = null;
public CodeElementCollection lockExits = new CodeElementCollection();
public bool isAnalyzed = false;
}
class RuleData
{
/// <summary>
/// Locks in the order of aqusition
/// </summary>
public ArrayList acqusitionStack = new ArrayList();
public RuleViolationCollection violations = new RuleViolationCollection();
}
private string GetLockSignature(CodeExpression lockExpression)
{
if(lockExpression is CodeNamedReferenceExpression)
return CodeNamedReferenceExpressionUtils.GetTargetReferencesAsString(lockExpression as CodeNamedReferenceExpression);
if(lockExpression is CodeThisReferenceExpression)
{
return "this";
}
return string.Empty;
}
private CodeDomWalker.WalkerCallbackReturn CollectReferencesToMonitorCalls(CodeElement codeElement, CodeDomWalker.CallBackNotificationType notificationType, CodeDomWalkerContext walkerContext,object applicationData)
{
if(notificationType == CodeDomWalker.CallBackNotificationType.OnElement)
{
if(codeElement is CodeMethodInvokeExpression)
{
string calledMethod = CodeNamedReferenceExpressionUtils.GetTargetReferencesAsString((codeElement as CodeMethodInvokeExpression).MethodReferenceExpression as CodeNamedReferenceExpression);
if(calledMethod == "Threading.Monitor.Enter" ||
calledMethod == "Monitor.Enter" ||
calledMethod == "System.Threading.Monitor.Enter" ||
calledMethod == "Threading.Monitor.TryEnter" ||
calledMethod == "Monitor.TryEnter" ||
calledMethod == "System.Threading.Monitor.TryEnter")
{
CodeExpression lockExpression = (codeElement as CodeMethodInvokeExpression).Arguments[0].Value;
string lockSignature = GetLockSignature(lockExpression);
RuleData ruleData = applicationData as RuleData;
LockData lockData = new LockData();
lockData.lockSignature = lockSignature;
lockData.lockExpression = lockExpression;
lockData.lockEnterance = codeElement as CodeMethodInvokeExpression;
ruleData.acqusitionStack.Insert(0,lockData);
}
if(calledMethod == "Threading.Monitor.Exit" || calledMethod == "Monitor.Exit" || calledMethod == "System.Threading.Monitor.Exit")
{
RuleData ruleData = applicationData as RuleData;
CodeExpression lockExpression = (codeElement as CodeMethodInvokeExpression).Arguments[0].Value;
string lockSignature = GetLockSignature(lockExpression);
if(ruleData.acqusitionStack.Count == 0)
{
RuleViolation violation = new RuleViolation(this,codeElement);
violation.Severity = RuleViolation.ViolationSeverity.Error;
violation.Description = ResourceManager.GetLocalizedString("ProtectLockReleaseRule|Violation|LockReleaseBeforeAquired");
ruleData.violations.Add(violation);
return CodeDomWalker.WalkerCallbackReturn.Next;
}
for(int i = 0; i < ruleData.acqusitionStack.Count; i++)
{
LockData lockData = ruleData.acqusitionStack[i] as LockData;
if(lockData.lockSignature == lockSignature)
{
lockData.lastLockExit = codeElement as CodeMethodInvokeExpression;
lockData.lockExits.Add(codeElement);
break;
}
if(lockData.lastLockExit == null)
{
RuleViolation violation = new RuleViolation(this,codeElement);
violation.Severity = RuleViolation.ViolationSeverity.Error;
violation.Description = ResourceManager.GetLocalizedString("ProtectLockReleaseRule|Violation|InvalidReleaseOrder");
ruleData.violations.Add(violation);
for(int enteredLockIndex = 0; enteredLockIndex < ruleData.acqusitionStack.Count; enteredLockIndex++)
{
LockData enteredLock = ruleData.acqusitionStack[enteredLockIndex] as LockData;
enteredLock.isAnalyzed = true;
}
}
}
}
}
}
return CodeDomWalker.WalkerCallbackReturn.Next;
}
private void AnalyzeLockUsage(RuleData ruleData)
{
//Check if same locks has been aquired twice
for(int i = 0; i < ruleData.acqusitionStack.Count; i++)
{
LockData lockData = ruleData.acqusitionStack[i] as LockData;
if(lockData.isAnalyzed)
continue;
for(int nestedLockIndex = i+1; nestedLockIndex < ruleData.acqusitionStack.Count; nestedLockIndex++)
{
LockData nestedLockData = ruleData.acqusitionStack[nestedLockIndex] as LockData;
if(nestedLockData.isAnalyzed)
continue;
if(lockData.lockSignature == nestedLockData.lockSignature)
{
RuleViolation violation = new RuleViolation(this,nestedLockData.lockEnterance);
violation.Severity = RuleViolation.ViolationSeverity.Error;
violation.Description = ResourceManager.GetLocalizedString("ProtectLockReleaseRule|Violation|UnnecessaryLockAcquisition",lockData.lockEnterance.SourcePosition.Line.ToString());
ruleData.violations.Add(violation);
nestedLockData.isAnalyzed = true;
lockData.isAnalyzed = true;
continue;
}
}
}
//Check if all locks has been released
for(int i = 0; i < ruleData.acqusitionStack.Count; i++)
{
LockData lockData = ruleData.acqusitionStack[i] as LockData;
if(lockData.lastLockExit == null)
{
RuleViolation violation = new RuleViolation(this,lockData.lockEnterance);
violation.Severity = RuleViolation.ViolationSeverity.Error;
violation.Description = ResourceManager.GetLocalizedString("ProtectLockReleaseRule|Violation|LockNotReleased");
ruleData.violations.Add(violation);
continue;
}
}
//Check if all exits are in finilize section
for(int i = 0; i < ruleData.acqusitionStack.Count; i++)
{
LockData lockData = ruleData.acqusitionStack[i] as LockData;
if(lockData.isAnalyzed)
continue;
ArrayList locksInFinilize = new ArrayList();
foreach(CodeElement exitCall in lockData.lockExits)
{
CodeElement[] trace = CodeElementTrace.GetCodeElementTrace(exitCall).Trace;
foreach(CodeElement traceFrame in trace)
{
if(traceFrame is CodeFinallyClause)
{
locksInFinilize.Add(exitCall);
break;
}
}
}
if(locksInFinilize.Count != 0 && lockData.lockExits.Count != locksInFinilize.Count)
{
RuleViolation violation = new RuleViolation(this,lockData.lockEnterance);
violation.Severity = RuleViolation.ViolationSeverity.Error;
violation.Description = ResourceManager.GetLocalizedString("ProtectLockReleaseRule|Violation|MultipleLockReleasesIncludingFinallyBlock");
ruleData.violations.Add(violation);
violation.ViolationData["LockData"] = lockData;
violation.ViolationData["ViolationType"] = "SomeProtected";
continue;
}
if(locksInFinilize.Count == 0 && lockData.lockExits.Count != 0)
{
RuleViolation violation = new RuleViolation(this,lockData.lockEnterance);
violation.AddCorrection(ResourceManager.GetLocalizedString("ProtectLockReleaseRule|Correction|MultipleLockReleasesNoFinallyBlock"),true,false);
violation.Severity = RuleViolation.ViolationSeverity.Error;
violation.Description = ResourceManager.GetLocalizedString("ProtectLockReleaseRule|Violation|MultipleLockReleasesNoFinallyBlock");
ruleData.violations.Add(violation);
violation.ViolationData["LockData"] = lockData;
violation.ViolationData["ViolationType"] = "NothingProtected";
continue;
}
}
}
public override RuleViolation[] Analyze(object codeElement, System.Threading.ManualResetEvent cancelAnalysisEvent)
{
RuleViolationCollection violations = new RuleViolationCollection();
CodeTypeMemberDeclaration typeMemberDeclarartion = codeElement as CodeTypeMemberDeclaration;
if(!IsCodeElementInRuleApplicabilityScope(typeMemberDeclarartion))
return noViolations;
Type elementType = codeElement.GetType();
foreach(PropertyInfo propertyInfo in elementType.GetProperties())
{
//If property does not have public getter - we are not intrested.
if(!propertyInfo.CanRead)
continue;
if(propertyInfo.PropertyType == typeof(CodeStatementBlock))
{
//Get property value.
CodeElement childElement = (CodeElement) propertyInfo.GetValue(codeElement,null);
//There is child! Walk this branch of the tree.
if(childElement != null)
{
RuleData ruleData = new RuleData();
CodeDomWalker.WalkCodeElement(childElement as CodeElement,new CodeDomWalker.WalkerCallback(CollectReferencesToMonitorCalls),ruleData);
AnalyzeLockUsage(ruleData);
violations.AddRange(ruleData.violations);
}
continue;
}
}
if(violations.Count == 0)
return noViolations;
else
return violations.ToArray();
}
private CodeTryStatement ProtectLockBlock(CodeElement firstReference,CodeElement lastReference,CodeEffector codeEffector)
{
CodeElement blockContainer = CodeElementUtils.GetElementsCommonContainer(firstReference,lastReference);
CodeElement[] trace = CodeElementTrace.GetCodeElementTrace(blockContainer).Trace;
CodeStatement containerStatement = CodeStatementUtils.GetElementStatement(blockContainer);
CodeTryStatement tryStatement = new CodeTryStatement();
CodeFinallyClause finallyClause = new CodeFinallyClause();
finallyClause.Statements = new CodeStatementBlock();
tryStatement.Statements = new CodeStatementBlock();
tryStatement.FinallyClause = finallyClause;
CodeExpressionStatement releaseLockStatement = new CodeExpressionStatement();
finallyClause.Statements.Statements.Add(CodeStatementUtils.GetElementStatement(lastReference).Clone() as CodeStatement);
if(containerStatement is CodeStatementBlock)
{
//add only statements in between first and last reference
bool addingStatements = false;
foreach(CodeStatement statement in (containerStatement as CodeStatementBlock).Statements)
{
if(addingStatements)
{
tryStatement.Statements.Statements.Add(statement.Clone() as CodeStatement);
codeEffector.DeleteCodeElement(statement);
CodeElement[] lastReferenceTrace = CodeElementTrace.GetCodeElementTrace(lastReference).Trace;
for(int i = 0; i < lastReferenceTrace.Length; i++)
{
if(lastReferenceTrace[i] == statement)
{
addingStatements = false;
break;
}
}
}
else
{
CodeElement[] firstReferenceTrace = CodeElementTrace.GetCodeElementTrace(firstReference).Trace;
for(int i = 0; i < firstReferenceTrace.Length; i++)
{
if(firstReferenceTrace[i] == statement)
{
tryStatement.Statements.Statements.Add(statement.Clone() as CodeStatement);
addingStatements = true;
codeEffector.ReplaceCodeElement(statement,tryStatement);
break;
}
}
}
}
}
else
{
CodeStatementBlock statementBlock = new CodeStatementBlock();
statementBlock.Statements.Add(tryStatement);
tryStatement.Statements.Statements.Add(containerStatement as CodeEmbeddedStatement);
codeEffector.ReplaceCodeElement(containerStatement,statementBlock);
}
return tryStatement;
}
public override void Correct(RuleViolationCorrection[] requestedCorrections, AnticipatingMinds.Genesis.Effectors.CodeEffector codeEffector, System.Threading.ManualResetEvent cancelCorrectionEvent)
{
codeEffector.BeginCodeChanges("");
string fileName = "";
foreach(RuleViolationCorrection requestedCorrection in requestedCorrections)
{
if(requestedCorrection.Violation.IsFixed)
continue;
if((string)requestedCorrection.Violation.ViolationData["ViolationType"] != "NothingProtected")
continue;
LockData lockData = requestedCorrection.Violation.ViolationData["LockData"] as LockData;
fileName = lockData.lockEnterance.SourcePosition.FileName;
CodeTryStatement tryStatement = ProtectLockBlock(lockData.lockEnterance,lockData.lockExits[lockData.lockExits.Count-1] as CodeElement,codeEffector);
codeEffector.PerformCodeChanges();
lockData.lockExits.Clear();
CodeDomWalker.WalkCodeElement(tryStatement.Statements,new CodeDomWalker.WalkerCallback(GetLockExitCallsCallback),lockData);
foreach(CodeElement lockExit in lockData.lockExits)
{
CodeStatement statementToDelete = CodeStatementUtils.GetElementStatement(lockExit);
CodeElement[] trace = CodeElementTrace.GetCodeElementTrace(statementToDelete).Trace;
if(statementToDelete.Parent is CodeStatementBlock)
codeEffector.DeleteCodeElement(statementToDelete);
else
codeEffector.ReplaceCodeElement(statementToDelete,new CodeStatementBlock());
}
codeEffector.PerformCodeChanges();
requestedCorrection.Violation.IsFixed = true;
}
codeEffector.CommitCodeChanges();
}
private CodeDomWalker.WalkerCallbackReturn GetLockExitCallsCallback(CodeElement codeElement, CodeDomWalker.CallBackNotificationType notificationType, CodeDomWalkerContext walkerContext,object applicationData)
{
if(notificationType == CodeDomWalker.CallBackNotificationType.OnElement)
{
if(codeElement is CodeMethodInvokeExpression)
{
string calledMethod = CodeNamedReferenceExpressionUtils.GetTargetReferencesAsString((codeElement as CodeMethodInvokeExpression).MethodReferenceExpression as CodeNamedReferenceExpression);
if(calledMethod == "Threading.Monitor.Exit" || calledMethod == "Monitor.Exit" || calledMethod == "System.Threading.Monitor.Exit")
{
LockData lockData = applicationData as LockData;
CodeExpression lockExpression = (codeElement as CodeMethodInvokeExpression).Arguments[0].Value;
if(lockData.lockSignature == GetLockSignature(lockExpression))
lockData.lockExits.Add(codeElement);
}
}
}
return CodeDomWalker.WalkerCallbackReturn.Next;
}
public override Type[] TargetedCodeElements
{
get
{
return targtedCodeElements;
}
}
public override Type[] ApplicableApplicabilityScopeTypes
{
get
{
return applicableApplicabilityScopeTypes;
}
}
private static Type[] applicableApplicabilityScopeTypes = {typeof(MethodApplicabilityScope),typeof(PropertyApplicabilityScope),typeof(EventApplicabilityScope),typeof(FileApplicabilityScope)};
private Type[] targtedCodeElements = new Type[1] {typeof(CodeTypeMemberDeclaration)};
private static RuleViolation[] noViolations = new RuleViolation[0];
}
}
|