internal void Traverse(SourceMethodBody body, BasicBlock rootBlock) { this.Traverse(rootBlock); //Now add declarations for any locals declared only on the body (for example temporary variables introduced by Unstacker). List<IStatement> prelude = new List<IStatement>(); var localsAndTemps = body.localVariablesAndTemporaries; this.AddDeclarationsWithInitialValues(localsAndTemps, rootBlock); foreach (var localDef in localsAndTemps) { if (this.declaredLocals.ContainsKey(localDef)) continue; LocalDeclarationStatement localDecl = new LocalDeclarationStatement(); localDecl.LocalVariable = localDef; prelude.Add(localDecl); } if (prelude.Count > 0) rootBlock.Statements.InsertRange(0, prelude); //TODO: use pdb info to insert them in the same order they appear in the source }
public override void TraverseChildren(IBlockStatement block) { BasicBlock basicBlock = (BasicBlock)block; List<ILocalDefinition>/*?*/ localsInCurrentScope = basicBlock.LocalVariables; if (localsInCurrentScope != null) { this.AddDeclarationsWithInitialValues(localsInCurrentScope, basicBlock); List<IStatement> prelude = new List<IStatement>(localsInCurrentScope.Count); foreach (ILocalDefinition localDef in localsInCurrentScope) { if (this.declaredLocals.ContainsKey(localDef)) continue; LocalDeclarationStatement localDecl = new LocalDeclarationStatement(); localDecl.LocalVariable = localDef; prelude.Add(localDecl); } if (prelude.Count > 0) basicBlock.Statements.InsertRange(0, prelude); //TODO: use pdb info to insert them in the same order they appear in the source } this.Traverse(basicBlock.Statements); }
public static IBlockStatement GetRidOfStack(SourceMethodBody methodBody, IBlockStatement block) { var me = new Unstacker(methodBody); var result = me.Rewrite(block); var stmts = new List<IStatement>(); foreach (var loc in me.createdLocals.Values) { var decl = new LocalDeclarationStatement() { InitialValue = null, LocalVariable = loc, }; stmts.Add(decl); } stmts.AddRange(result.Statements); var newBlock = new BlockStatement() { Statements = stmts, Locations = new List<ILocation>(result.Locations), }; return newBlock; }
public override IExpression Rewrite(IArrayIndexer arrayIndexer) { if (ExpressionTraverser.IsAtomicInstance(arrayIndexer.IndexedObject)) return arrayIndexer; // arrayIndexer == AI(inst, [index]), i.e., inst[index0, index1,...] // return { loc := e; [assert loc != null;] | AI(BE(null,loc), [index]) } // where e is the rewritten array instance var e = base.Rewrite(arrayIndexer.IndexedObject); var loc = new LocalDefinition() { Name = this.host.NameTable.GetNameFor("_loc" + this.sink.LocalCounter.ToString()), Type = e.Type }; var locDecl = new LocalDeclarationStatement() { InitialValue = e, LocalVariable = loc, }; return new BlockExpression() { BlockStatement = new BlockStatement() { Statements = new List<IStatement> { locDecl }, }, Expression = new ArrayIndexer() { IndexedObject = new BoundExpression() { Definition = loc, Instance = null, Type = loc.Type, }, Indices = new List<IExpression>(arrayIndexer.Indices), Type = arrayIndexer.Type, }, }; }
public override IStatement Visit(LocalDeclarationStatement localDeclarationStatement) { var localDefinition = localDeclarationStatement.LocalVariable; if (this.cachedDelegateFieldsOrLocals.ContainsKey(localDefinition.Name.Value)) return CodeDummy.Block; else return base.Visit(localDeclarationStatement); }
/// <summary> /// Visits the specified local declaration statement. /// </summary> /// <param name="localDeclarationStatement">The local declaration statement.</param> /// <returns></returns> protected virtual IStatement DeepCopy(LocalDeclarationStatement localDeclarationStatement) { localDeclarationStatement.LocalVariable = this.Substitute(localDeclarationStatement.LocalVariable); if (localDeclarationStatement.InitialValue != null) localDeclarationStatement.InitialValue = Substitute(localDeclarationStatement.InitialValue); return localDeclarationStatement; }
public override IExpression Rewrite(IBoundExpression boundExpression) { if (boundExpression.Instance == null || ExpressionTraverser.IsAtomicInstance(boundExpression.Instance)) return boundExpression; // boundExpression == BE(inst, def), i.e., inst.def // return { loc := e; [assert loc != null;] | BE(BE(null,loc), def) }, i.e., "loc := e; loc.def" // where e is the rewritten inst var e = base.Rewrite(boundExpression.Instance); var loc = new LocalDefinition() { Name = this.host.NameTable.GetNameFor("_loc" + this.sink.LocalCounter.ToString()), Type = e.Type, }; var locDecl = new LocalDeclarationStatement() { InitialValue = e, LocalVariable = loc, }; return new BlockExpression() { BlockStatement = new BlockStatement() { Statements = new List<IStatement> { locDecl }, }, Expression = new BoundExpression() { Definition = boundExpression.Definition, Instance = new BoundExpression() { Definition = loc, Instance = null, Type = loc.Type, }, Type = boundExpression.Type, }, }; }
/// <summary> /// The source expression "new C(){ f1 = e1, f2 = e2, ... }" (where the f's can be fields /// or properties) turns into "cgl = new C(); cgl.f1 = e1; cg1.f2 = e2; ...". /// ("cgl" means "compiler-generated local".) /// Turn it into a block expression whose Statements are the statements above (but where /// the first one becomes a local declaration statement), and with an Expression that is /// just the local, cgl', where cgl' is a freshly created local. /// </summary> private bool ReplaceCompilerGeneratedLocalUsedForInitializersPattern(BlockStatement b) { Contract.Requires(b != null); bool replacedPattern = false; var statements = b.Statements; for (int i = 0; i < statements.Count - 1; i++) { var expressionStatement = statements[i] as ExpressionStatement; if (expressionStatement == null) continue; var assignment = expressionStatement.Expression as Assignment; if (assignment == null) continue; var local = assignment.Target.Definition as ILocalDefinition; if (local == null || local is CapturedLocalDefinition) continue; if (this.numberOfAssignmentsToLocal[local] != 1) continue; if (this.sourceLocationProvider != null) { bool isCompilerGenerated; var sourceName = this.sourceLocationProvider.GetSourceNameFor(local, out isCompilerGenerated); if (!isCompilerGenerated) continue; } var createObject = assignment.Source as ICreateObjectInstance; if (createObject == null) continue; if (!this.singleUseExpressionChecker.ExpressionCanBeMovedAndDoesNotReference(assignment.Source, local)) continue; var j = 1; while (i + j < statements.Count - 1 && IsAssignmentToFieldOrProperty(local, statements[i + j])) j++; if (j == 1) continue; if (this.numberOfReferencesToLocal[local] != (uint)j) continue; Contract.Assume(i + j < statements.Count); //i < statements.Count-1 and (j == 1 or the loop above established i+j < statements.Count-1) Contract.Assume(statements[i + j] != null); if (LocalFinder.LocalOccursIn(statements[i+j], local) && this.singleAssignmentReferenceFinder.LocalCanBeReplacedIn(statements[i + j], local)) { var newLocal = new LocalDefinition() { Name = this.host.NameTable.GetNameFor(local.Name.Value + "_prime"), MethodDefinition = local.MethodDefinition, Type = local.Type, }; var lds = new LocalDeclarationStatement() { InitialValue = assignment.Source, LocalVariable = newLocal, }; var stmts = new List<IStatement>(j) { lds, }; var boundExpression = new BoundExpression() { Definition = newLocal, Instance = null, Type = newLocal.Type, }; foreach (var s in statements.GetRange(i + 1, j - 1)) { Contract.Assume(s != null); this.singleAssignmentLocalReplacer.Replace(boundExpression, local, s); stmts.Add(s); } var blockExpression = new BlockExpression() { BlockStatement = new BlockStatement() { Statements = stmts, }, Expression = new BoundExpression() { Definition = newLocal, Instance = null, Type = newLocal.Type, }, Type = newLocal.Type, }; if (this.singleAssignmentLocalReplacer.Replace(blockExpression, local, statements[i + j])) { this.numberOfAssignmentsToLocal[newLocal] = 1; this.numberOfReferencesToLocal[newLocal] = (uint)j; this.numberOfAssignmentsToLocal[local]--; this.numberOfReferencesToLocal[local] = 0; statements.RemoveRange(i, j); replacedPattern = true; } else Contract.Assume(false); // replacement should succeed since the combination of LocalOccursIn and LocalCanBeReplacedIn returned true } } return replacedPattern; }
/// <summary> /// Visits the specified local declaration statement. /// </summary> /// <param name="localDeclarationStatement">The local declaration statement.</param> public override void Visit(ILocalDeclarationStatement localDeclarationStatement) { LocalDeclarationStatement mutableLocalDeclarationStatement = new LocalDeclarationStatement(localDeclarationStatement); this.resultStatement = this.myCodeCopier.DeepCopy(mutableLocalDeclarationStatement); }
/// <summary> /// Visits the specified local declaration statement. /// </summary> /// <param name="localDeclarationStatement">The local declaration statement.</param> /// <returns></returns> public virtual IStatement Visit(LocalDeclarationStatement localDeclarationStatement) { localDeclarationStatement.LocalVariable = this.Visit(localDeclarationStatement.LocalVariable); if (localDeclarationStatement.InitialValue != null) localDeclarationStatement.InitialValue = Visit(localDeclarationStatement.InitialValue); return localDeclarationStatement; }
/// <summary> /// Rewrites the children of the given local declaration statement. /// </summary> public virtual void RewriteChildren(LocalDeclarationStatement localDeclarationStatement) { this.RewriteChildren((Statement)localDeclarationStatement); localDeclarationStatement.LocalVariable = this.Rewrite(localDeclarationStatement.LocalVariable); if (localDeclarationStatement.InitialValue != null) localDeclarationStatement.InitialValue = this.Rewrite(localDeclarationStatement.InitialValue); }
/// <summary /> public override IStatement Rewrite(IForEachStatement forEachStatement) { ILocalDefinition foreachLocal; var key = forEachStatement.Collection.Type.InternedKey; ITypeReference enumeratorType; IMethodReference getEnumerator; IMethodReference getCurrent; var gtir = forEachStatement.Collection.Type as IGenericTypeInstanceReference; if (gtir != null) { var typeArguments = gtir.GenericArguments; ITypeReference genericEnumeratorType = new Immutable.GenericTypeInstanceReference(this.host.PlatformType.SystemCollectionsGenericIEnumerator, typeArguments, this.host.InternFactory); ITypeReference genericEnumerableType = new Immutable.GenericTypeInstanceReference(this.host.PlatformType.SystemCollectionsGenericIEnumerable, typeArguments, this.host.InternFactory); enumeratorType = genericEnumeratorType; getEnumerator = new SpecializedMethodReference() { CallingConvention = CallingConvention.HasThis, ContainingType = genericEnumerableType, InternFactory = this.host.InternFactory, Name = this.host.NameTable.GetNameFor("GetEnumerator"), Parameters = new List<IParameterTypeInformation>(), Type = genericEnumeratorType, UnspecializedVersion = new MethodReference() { CallingConvention = CallingConvention.HasThis, ContainingType = this.host.PlatformType.SystemCollectionsGenericIEnumerable, InternFactory = this.host.InternFactory, Name = this.host.NameTable.GetNameFor("GetEnumerator"), Parameters = new List<IParameterTypeInformation>(), Type = this.host.PlatformType.SystemCollectionsGenericIEnumerator, }, }; var getEnumerator2 = (IMethodReference) IteratorHelper.First(genericEnumerableType.ResolvedType.GetMembersNamed(this.host.NameTable.GetNameFor("GetEnumerator"), false)); getEnumerator = getEnumerator2; getCurrent = (IMethodReference) IteratorHelper.First(genericEnumeratorType.ResolvedType.GetMembersNamed(this.host.NameTable.GetNameFor("get_Current"), false)); } else { enumeratorType = this.host.PlatformType.SystemCollectionsIEnumerator; getEnumerator = new MethodReference() { CallingConvention = CallingConvention.HasThis, ContainingType = enumeratorType, InternFactory = this.host.InternFactory, Name = this.host.NameTable.GetNameFor("GetEnumerator"), Parameters = new List<IParameterTypeInformation>(), Type = this.host.PlatformType.SystemCollectionsIEnumerable, }; getCurrent = new MethodReference() { CallingConvention = CallingConvention.HasThis, ContainingType = enumeratorType, InternFactory = this.host.InternFactory, Name = this.host.NameTable.GetNameFor("get_Current"), Parameters = new List<IParameterTypeInformation>(), Type = this.host.PlatformType.SystemObject, }; } var initializer = new MethodCall() { Arguments = new List<IExpression>(), IsStaticCall = false, IsVirtualCall = true, MethodToCall = getEnumerator, ThisArgument = forEachStatement.Collection, Type = enumeratorType, }; IStatement initialization; if (!this.foreachLocals.TryGetValue(key, out foreachLocal)) { foreachLocal = new LocalDefinition() { Type = enumeratorType, Name = this.host.NameTable.GetNameFor("CS$5$" + this.foreachLocals.Count) }; this.foreachLocals.Add(key, foreachLocal); initialization = new LocalDeclarationStatement() { InitialValue = initializer, LocalVariable = foreachLocal, }; } else { initialization = new ExpressionStatement() { Expression = new Assignment() { Source = initializer, Target = new TargetExpression() { Definition = foreachLocal, Instance = null, Type = foreachLocal.Type, }, Type = foreachLocal.Type, }, }; } var newStmts = new List<IStatement>(); newStmts.Add(new ExpressionStatement(){ Expression = new Assignment(){ Source = new MethodCall(){ Arguments = new List<IExpression>(), IsStaticCall = false, IsVirtualCall = true, MethodToCall = getCurrent, ThisArgument = new BoundExpression(){ Definition = foreachLocal, Instance = null, }, Type = forEachStatement.Variable.Type, }, Target = new TargetExpression(){ Definition = forEachStatement.Variable, Instance = null, }, Type = forEachStatement.Variable.Type, }, }); newStmts.Add(forEachStatement.Body); var newBody = new BlockStatement(){ Statements = newStmts,}; var result = new BlockStatement() { Statements = new List<IStatement>(){ initialization, new TryCatchFinallyStatement(){ TryBody = new BlockStatement() { Statements = new List<IStatement>(){ new WhileDoStatement(){ Body = newBody, Condition = new MethodCall(){ Arguments = new List<IExpression>(), IsStaticCall = false, IsVirtualCall = true, MethodToCall = moveNext, ThisArgument = new BoundExpression(){ Definition = foreachLocal, Instance = null, }, Type = this.host.PlatformType.SystemBoolean, }, }, }, }, FinallyBody = new BlockStatement() { Statements = new List<IStatement>(){ new ConditionalStatement(){ Condition = new Equality(){ LeftOperand = new BoundExpression(){ Definition = foreachLocal, Instance = null, Type = foreachLocal.Type, }, RightOperand = new CompileTimeConstant(){ Type = foreachLocal.Type, Value = null, }, Type = this.host.PlatformType.SystemBoolean, }, FalseBranch = new EmptyStatement(), TrueBranch = new ExpressionStatement(){ Expression = new MethodCall(){ Arguments = new List<IExpression>(), IsStaticCall = false, IsVirtualCall = true, MethodToCall = this.disposeMethod, ThisArgument = new BoundExpression(){ Definition = foreachLocal, Instance = null, }, Type = this.host.PlatformType.SystemVoid, }, }, }, }, }, }, }, }; return result; }
/// <summary> /// Rewrites the given assignment expression. /// </summary> public override IExpression Rewrite(IAssignment assignment) { var targetInstance = assignment.Target.Instance; var result = base.Rewrite(assignment); if (targetInstance == null && assignment.Target.Instance != null) { //The target now pushes something onto the stack that was not there before the rewrite. //It the right hand side uses the stack, then it will not see the stack it expected. //If so, we need to evaluate the right handside and squirrel it away in a temp before executing //the actual assignment. var popFinder = new PopFinder(); popFinder.Traverse(assignment.Source); if (popFinder.foundAPop) { var temp = new LocalDefinition() { Name = this.host.NameTable.GetNameFor("PopTemp"+this.popTempCounter++), Type = assignment.Source.Type }; var localDeclarationStatement = new LocalDeclarationStatement() { LocalVariable = temp, InitialValue = assignment.Source }; var blockStatement = new BlockStatement(); blockStatement.Statements.Add(localDeclarationStatement); Contract.Assume(assignment is Assignment); ((Assignment)assignment).Source = new BoundExpression() { Definition = temp, Type = temp.Type }; return new BlockExpression() { BlockStatement = blockStatement, Expression = assignment, Type = assignment.Type }; } } return result; }
public override void TraverseChildren(IBlockStatement block) { Contract.Assume(block is BlockStatement); var mutableBlock = (BlockStatement)block; this.Traverse(mutableBlock.Statements); foreach (var pair in this.declaringBlockMap) { if (pair.Value != mutableBlock) continue; var boundExpr = this.closureFieldToLocalOrParameterMap[pair.Key] as BoundExpression; if (boundExpr == null) { Contract.Assume(false); continue; } var local = boundExpr.Definition as ILocalDefinition; if (local == null) { Contract.Assume(false); continue; } var localDecl = new LocalDeclarationStatement() { LocalVariable = local }; mutableBlock.Statements.Insert(0, localDecl); } }
/// <summary> /// Appends the statements afters the calls used to calculate the max of the tmp of all the calls /// </summary> protected void AppendStatementsTmpCopy(IMethodDefinition method, List<StatementTmpCopyInformation> statementsTmpCopy, Dictionary<IStatement, IBlockStatement> statementsContainingBlocks, Dictionary<string, GlobalPolyInfo> globalPolyInfoTmp) { var block = ((BasicBlock)((Microsoft.Cci.ILToCodeModel.SourceMethodBody)method.Body).Block); var methodStatements = block.Statements; var int32Type = _host.PlatformType.SystemInt32; var callNumber = 1; var localsInsertedByType = new Dictionary<ITypeReference, List<LocalDeclarationStatement>>(); foreach (var statementCopyInformation in statementsTmpCopy) { var stmt = statementCopyInformation.AfterStatement; var statements = ((BlockStatement)statementsContainingBlocks[stmt]).Statements; var index = statements.IndexOf(stmt); if (index >= 0) { IExpression initialValue = new BoundExpression() { Type = int32Type, Definition = GetTmpField(statementCopyInformation.CalledMethod, statementCopyInformation.TmpType) }; var localDecl = new LocalDeclarationStatement() { InitialValue = initialValue, LocalVariable = new LocalDefinition() { Type = int32Type, Name = _host.NameTable.GetNameFor("tmp_call_" + callNumber) } }; callNumber++; var tmpType = statementCopyInformation.TmpType; if (!localsInsertedByType.ContainsKey(tmpType)) { localsInsertedByType.Add(tmpType, new List<LocalDeclarationStatement>()); } localsInsertedByType[tmpType].Add(localDecl); if (!statementCopyInformation.InsideLoop) { statements.Insert(index + 1, localDecl); var calledMethodName = statementCopyInformation.CalledMethodReference.ToString(); var contractLocalExpr = TranslateMethodContractToLocalExpression( statementCopyInformation.CalledMethodReference, stmt, _memoryContractsInformation.MethodsTmpContracts[calledMethodName][statementCopyInformation.TmpType], statementCopyInformation.CalledMethodArguments); if (!_currentInstrInfo.GlobalPolyInfoTmp.ContainsKey(tmpType.ToString())) { _currentInstrInfo.GlobalPolyInfoTmp.Add(tmpType.ToString(), new GlobalPolyInfo()); } _currentInstrInfo.GlobalPolyInfoTmp[tmpType.ToString()].MaxCalls.Add(contractLocalExpr); } else { localDecl.InitialValue = new CompileTimeConstant() { Type = int32Type, Value = 0 }; var loopContainingStatements = ((BasicBlock)statementsContainingBlocks[statementCopyInformation.LoopStartAt]).Statements; loopContainingStatements.Insert(0, localDecl); bool doRegularInstrumentation = true; if (!_currentInstrInfo.GlobalPolyInfoTmp.ContainsKey(tmpType.ToString())) { _currentInstrInfo.GlobalPolyInfoTmp.Add(tmpType.ToString(), new GlobalPolyInfo()); } if (statementCopyInformation.LoopInvariants != null) { //invariants available, calculate the max directly and insert the assignment after the loop var calledMethodName = statementCopyInformation.CalledMethodReference.ToString(); if (_memoryContractsInformation.MethodsTmpContracts.ContainsKey(calledMethodName) && _memoryContractsInformation.MethodsTmpContracts[calledMethodName].ContainsKey(statementCopyInformation.TmpType)) { var contractLocalExpr = TranslateMethodContractToLocalExpression( statementCopyInformation.CalledMethodReference, stmt, _memoryContractsInformation.MethodsTmpContracts[calledMethodName][statementCopyInformation.TmpType], statementCopyInformation.CalledMethodArguments); var maxExprPolyCond = PolytopesCalculator.MaxOver(contractLocalExpr, statementCopyInformation.LoopInvariants, GetMethodFreeVars(_currentMethod)); IExpression maxPolyExpr = null; IExpression maxCondExpr = null; if (maxExprPolyCond != null) { maxPolyExpr = ExpressionGenerator.GenerateExpressionFromString(maxExprPolyCond.Poly, _host, _currentMethod); maxCondExpr = ExpressionGenerator.GenerateExpressionFromString(maxExprPolyCond.Cond, _host, _currentMethod); } index = loopContainingStatements.IndexOf(statementCopyInformation.LoopEndAt); if (index >= 0 && maxPolyExpr != null && maxCondExpr != null) { doRegularInstrumentation = false; loopContainingStatements.Insert(index + 1, new ConditionalStatement() { Condition = maxCondExpr, TrueBranch = new ExpressionStatement() { Expression = new Assignment() { Type = int32Type, Source = maxPolyExpr, Target = new TargetExpression() { Type = int32Type, Definition = localDecl.LocalVariable } } }, FalseBranch = new EmptyStatement() }); //store in the global poly info _currentInstrInfo.GlobalPolyInfoTmp[tmpType.ToString()].MaxCallsLoops.Add(maxExprPolyCond); } } } if (doRegularInstrumentation) { //no invariants available or could not calculate max, calculate instrumenting Math.Max calls if (statementCopyInformation.InsideLoop) { _currentInstrInfo.GlobalPolyInfoTmp[tmpType.ToString()].AllContractsRequiredAvailable = false; } index = statements.IndexOf(stmt); //adjust the index, it could have changed because of the previous localDecl insertion statements.Insert(index + 1, new ExpressionStatement() { Expression = new Assignment() { Type = int32Type, Source = BuildMaxCall(new BoundExpression() { Definition = localDecl.LocalVariable, Type = int32Type }, initialValue), Target = new TargetExpression() { Type = int32Type, Definition = localDecl.LocalVariable } } }); } } } } var appendToStatementList = ((BasicBlock)statementsContainingBlocks[_currentStatement]).Statements; foreach (var tmpType in localsInsertedByType.Keys) { bool doRegularInstrumentation = true; var tmpField = GetTmpField(method, tmpType); var globalInfoForTmp = globalPolyInfoTmp[tmpType.ToString()]; //check if we can calculate the max with the polytopes calculator if (globalInfoForTmp.AllContractsRequiredAvailable) { var polys = new List<string>(); var conds = new List<string>(); conds.AddRange(GetMethodRequiresExprs(method)); polys.AddRange(globalInfoForTmp.MaxCalls); foreach (var poly in globalInfoForTmp.MaxCallsLoops) { polys.Add(poly.Poly); conds.Add(poly.Cond); } var max = PolytopesCalculator.MaxPoly(polys, conds, GetMethodFreeVars(method)); if (max != null) { var condExpr = ExpressionGenerator.GenerateExpressionFromString(max.Cond, _host, method); var maxExpr = ExpressionGenerator.GenerateExpressionFromString(max.Poly, _host, method); if (condExpr != null && maxExpr != null) { InsertStatementAtBottom(appendToStatementList, new ConditionalStatement() { Condition = condExpr, TrueBranch = new ExpressionStatement() { Expression = new Assignment() { Type = int32Type, Source = new Addition() { Type = int32Type, LeftOperand = new BoundExpression() { Type = int32Type, Definition = tmpField }, RightOperand = maxExpr }, Target = new TargetExpression() { Type = int32Type, Definition = tmpField } } }, FalseBranch = new EmptyStatement() }); doRegularInstrumentation = false; } } } if (doRegularInstrumentation) { var maxExpr = GetMaxExpressionOverLocals(localsInsertedByType[tmpType]); //add the statement tmpField += Math.Max(...); InsertStatementAtBottom(appendToStatementList, new ExpressionStatement() { Expression = new Assignment() { Type = int32Type, Source = new Addition() { Type = int32Type, LeftOperand = new BoundExpression() { Type = int32Type, Definition = tmpField }, RightOperand = maxExpr }, Target = new TargetExpression() { Type = int32Type, Definition = tmpField } } }); } } }
/// <summary> /// Create the body of the generic version of GetEnumerator for the iterator closure class. /// /// The body's pseudo code. /// { /// if (Thread.CurrentThread.ManagedThreadId == this.l_initialThreadId AND this.state == -2) { /// this.state = 0; /// return this; /// } /// else { /// return a new copy of the iterator instance with state being zero. /// } /// } /// </summary> private BlockStatement GetBodyOfGenericGetEnumerator(IteratorClosureInformation iteratorClosure) { var thisDotState = new BoundExpression() { Definition = iteratorClosure.StateFieldReference, Instance = new ThisReference(), Type = this.host.PlatformType.SystemInt32 }; var thisDotThreadId = new BoundExpression() { Definition = iteratorClosure.InitThreadIdFieldReference, Instance = new ThisReference(), Type = this.host.PlatformType.SystemInt32 }; var currentThreadId = new MethodCall() { MethodToCall = ThreadDotManagedThreadId.Getter, ThisArgument = ThreadDotCurrentThread, Type = this.host.PlatformType.SystemInt32 }; var stateEqMinus2 = new Equality() { LeftOperand = thisDotState, RightOperand = new CompileTimeConstant() { Type = this.host.PlatformType.SystemInt32, Value = -2 }, Type = this.host.PlatformType.SystemBoolean }; var threadIdEqCurrentThreadId = new Equality { LeftOperand = thisDotThreadId, RightOperand = currentThreadId, Type = this.host.PlatformType.SystemBoolean }; var thisDotStateEq0 = new ExpressionStatement() { Expression = new Assignment() { Source = new CompileTimeConstant() { Type = this.host.PlatformType.SystemInt32, Value = 0 }, Target = new TargetExpression() { Definition = iteratorClosure.StateFieldReference, Instance = new ThisReference(), Type = this.host.PlatformType.SystemInt32 }, Type = this.host.PlatformType.SystemInt32 }, }; var returnThis = new BlockStatement(); returnThis.Statements.Add(thisDotStateEq0); returnThis.Statements.Add(new ReturnStatement() { Expression = new ThisReference() }); var returnNew = new BlockStatement(); var args = new List<IExpression>(); args.Add(new CompileTimeConstant() { Value = 0, Type = this.host.PlatformType.SystemInt32 }); var closureInstanceLocalDecl = new LocalDeclarationStatement() { LocalVariable = new LocalDefinition() { Name = this.host.NameTable.GetNameFor("local0"), Type = iteratorClosure.ClosureDefinitionReference }, InitialValue = new CreateObjectInstance() { MethodToCall = iteratorClosure.ConstructorReference, Arguments = args, Type = iteratorClosure.ClosureDefinitionReference } }; var returnNewClosureInstance = new ReturnStatement() { Expression = new BoundExpression() { Instance = null, Type = iteratorClosure.ClosureDefinitionReference, Definition = closureInstanceLocalDecl.LocalVariable } }; returnNew.Statements.Add(closureInstanceLocalDecl); if (!method.IsStatic) { ExpressionStatement assignThisDotThisToNewClosureDotThis = new ExpressionStatement() { Expression = new Assignment() { Source = new BoundExpression() { Definition = iteratorClosure.ThisFieldReference, Instance = new ThisReference(), Type = iteratorClosure.ClosureDefinitionReference }, Type = iteratorClosure.ClosureDefinition, Target = new TargetExpression() { Instance = new BoundExpression() { Instance = null, Definition = closureInstanceLocalDecl.LocalVariable, Type = iteratorClosure.ClosureDefinitionReference }, Definition = iteratorClosure.ThisFieldReference, Type = iteratorClosure.ClosureDefinitionReference } } }; returnNew.Statements.Add(assignThisDotThisToNewClosureDotThis); } returnNew.Statements.Add(returnNewClosureInstance); ConditionalStatement returnThisOrNew = new ConditionalStatement() { Condition = new Conditional() { Condition = stateEqMinus2, ResultIfTrue = threadIdEqCurrentThreadId, ResultIfFalse = new CompileTimeConstant() { Type = this.host.PlatformType.SystemBoolean, Value = false }, Type = this.host.PlatformType.SystemBoolean }, TrueBranch = returnThis, FalseBranch = returnNew }; BlockStatement block = new BlockStatement(); block.Statements.Add(returnThisOrNew); return block; }
/// <summary> /// Create the new body of the iterator method. /// </summary> /// <remarks> /// Pseudo code: /// iteratorClosureLocal = new Closure(0); /// iteratorClosureLocal.field = parameter; // for each parameter including this. /// return iteratorClosureLocal; /// </remarks> private BlockStatement CreateNewIteratorMethodBody(IteratorClosureInformation iteratorClosure) { BlockStatement result = new BlockStatement(); // iteratorClosureLocal = new IteratorClosure(0); LocalDefinition localDefinition = new LocalDefinition() { Name = this.host.NameTable.GetNameFor("iteratorClosureLocal"), Type = GetClosureTypeReferenceFromIterator(iteratorClosure), }; CreateObjectInstance createObjectInstance = new CreateObjectInstance() { MethodToCall = GetMethodReference(iteratorClosure, iteratorClosure.Constructor), Type = localDefinition.Type }; // the start state depends on whether the iterator is an IEnumerable or an IEnumerator. For the former, // it must be created in state -2. Then it is the GetEnumerator method that puts it into its // "start" state, i.e., state 0. var startState = this.isEnumerable ? -2 : 0; createObjectInstance.Arguments.Add(new CompileTimeConstant() { Value = startState, Type = this.host.PlatformType.SystemInt32 }); LocalDeclarationStatement localDeclarationStatement = new LocalDeclarationStatement() { InitialValue = createObjectInstance, LocalVariable = localDefinition }; result.Statements.Add(localDeclarationStatement); // Generate assignments to closure instance's fields for each of the parameters captured by the closure. foreach (object capturedLocalOrParameter in FieldForCapturedLocalOrParameter.Keys) { BoundField boundField = FieldForCapturedLocalOrParameter[capturedLocalOrParameter]; Assignment assignment; ITypeReference localOrParameterType = GetLocalOrParameterType(capturedLocalOrParameter); if (capturedLocalOrParameter is ILocalDefinition) continue; if (capturedLocalOrParameter is IThisReference) { var thisR = new ThisReference(); IExpression thisValue = thisR; if (!this.method.ContainingTypeDefinition.IsClass) { thisValue = new AddressDereference() { Address = thisR, Type = this.method.ContainingTypeDefinition.IsGeneric ? (ITypeReference)this.method.ContainingTypeDefinition.InstanceType : (ITypeReference)this.method.ContainingTypeDefinition }; } assignment = new Assignment { Source = thisValue, Type = this.method.ContainingType, Target = new TargetExpression() { Definition = GetFieldReference(iteratorClosure, boundField.Field), Type = this.method.ContainingType, Instance = new BoundExpression() { Type = localDefinition.Type, Instance = null, Definition = localDefinition, IsVolatile = false } }, }; } else { assignment = new Assignment { Source = new BoundExpression() { Definition = capturedLocalOrParameter, Instance = null, IsVolatile = false, Type = localOrParameterType }, Type = localOrParameterType, Target = new TargetExpression() { Definition = GetFieldReference(iteratorClosure, boundField.Field), Type = localOrParameterType, Instance = new BoundExpression() { Type = localDefinition.Type, Instance = null, Definition = localDefinition, IsVolatile = false } }, }; } ExpressionStatement expressionStatement = new ExpressionStatement() { Expression = assignment }; result.Statements.Add(expressionStatement); } // Generate: return iteratorClosureLocal; result.Statements.Add(new ReturnStatement() { Expression = new BoundExpression() { Definition = localDeclarationStatement.LocalVariable, Instance = null, Type = localDeclarationStatement.LocalVariable.Type } }); return result; }
public override void RewriteChildren(LocalDeclarationStatement localDeclarationStatement) { // then we don't need to replace this local, so put a dummy value in the table // so we don't muck with it. var loc = localDeclarationStatement.LocalVariable; // locals don't get removed (not tracking scopes) so if a local definition is re-used // (say in two for-loops), then we'd be trying to add the same key twice // shouldn't ever happen except for when the remaining body of the method is being // visited. if (!this.tableForLocalDefinition.ContainsKey(loc)) { this.tableForLocalDefinition.Add(loc, null); } if (localDeclarationStatement.InitialValue != null) { localDeclarationStatement.InitialValue = this.Rewrite(localDeclarationStatement.InitialValue); } return; }
public override IStatement Visit(LocalDeclarationStatement localDeclarationStatement) { ILocalDefinition local = localDeclarationStatement.LocalVariable; if (0 < this.currentClosureLocals.Count) { //if this temp was introduced to hold a copy of the closure local, then delete it var deleteIt = false; foreach (var currentClosureLocal in this.currentClosureLocals.Keys) { if (TypeHelper.TypesAreEquivalent(currentClosureLocal.Type, local.Type)) { deleteIt = true; break; } // Or it might be that we are visiting the method that is being turned back into a lambda // and it might be a temp introduced to hold "this" because of decompilation inadequacies. // (E.g., if there was "f++" operator in the lambda for a captured local/parameter, then // it becomes "ldarg0; dup" in the IL in the closure class and that decompiles to // "t1 := this; t2 := t1; t1.f := t2.f + 1". ITypeReference t1 = UnspecializedMethods.AsUnspecializedTypeReference(currentClosureLocal.Type); ITypeReference t2 = UnspecializedMethods.AsUnspecializedTypeReference(local.Type); if (t1 == t2) { deleteIt = true; break; } } if (deleteIt) { if (localDeclarationStatement.InitialValue == null) { // then need to delete all assignments to this local because they are the // real initialization. this.currentClosureLocals[local] = true; } return CodeDummy.Block; } } base.Visit(localDeclarationStatement); int numberOfAssignments = 0; if (!this.sourceMethodBody.numberOfAssignments.TryGetValue(local, out numberOfAssignments) || numberOfAssignments > 1) return localDeclarationStatement; int numReferences = 0; this.sourceMethodBody.numberOfReferences.TryGetValue(local, out numReferences); if (this.sourceMethodBody.sourceLocationProvider != null) { bool isCompilerGenerated = false; this.sourceMethodBody.sourceLocationProvider.GetSourceNameFor(local, out isCompilerGenerated); if (!isCompilerGenerated) return localDeclarationStatement; } var val = localDeclarationStatement.InitialValue; if (val is CompileTimeConstant || val is TypeOf || val is ThisReference) { this.expressionToSubstituteForCompilerGeneratedSingleAssignmentLocal.Add(local, val); return CodeDummy.Block; //Causes the caller to omit this statement from the containing statement list. } if (numReferences == 0 && val == null) return CodeDummy.Block; //unused declaration return localDeclarationStatement; }
private void FindCapturedLocals(List<IStatement> statements) { ILocalDefinition/*?*/ locDef = null; IFieldReference/*?*/ fieldRef = null; INestedTypeReference/*?*/ closureType = null; int i = 0; while (i < statements.Count && closureType == null) { var statement = statements[i++]; var locDecl = statement as LocalDeclarationStatement; if (locDecl == null) { var exprStatement = statement as ExpressionStatement; if (exprStatement == null) continue; var assignment = exprStatement.Expression as Assignment; if (assignment == null) continue; if (!(assignment.Source is ICreateObjectInstance)) continue; locDef = assignment.Target.Definition as ILocalDefinition; if (locDef != null) closureType = UnspecializedMethods.AsUnspecializedNestedTypeReference(locDef.Type); else { fieldRef = assignment.Target.Definition as IFieldReference; if (fieldRef == null || !(assignment.Target.Instance is IThisReference)) continue; closureType = UnspecializedMethods.AsUnspecializedNestedTypeReference(fieldRef.Type); } } else { if (!(locDecl.InitialValue is ICreateObjectInstance)) continue; locDef = locDecl.LocalVariable; closureType = UnspecializedMethods.AsUnspecializedNestedTypeReference(locDef.Type); } } if (closureType == null) return; //REVIEW: need to avoid resolving types that are not defined in the module we are analyzing. ITypeReference t1 = UnspecializedMethods.AsUnspecializedTypeReference(closureType.ContainingType.ResolvedType); ITypeReference t2 = UnspecializedMethods.AsUnspecializedTypeReference(this.remover.containingType); if (!TypeHelper.TypesAreEquivalent(t1, t2)) { var nt2 = t2 as INestedTypeReference; if (nt2 == null || !TypeHelper.TypesAreEquivalent(t1, nt2.ContainingType)) return; } var resolvedClosureType = closureType.ResolvedType; if (!UnspecializedMethods.IsCompilerGenerated(resolvedClosureType)) return; //Check if this is an iterator creating its state class, rather than just a method creating a closure class foreach (var iface in resolvedClosureType.Interfaces) { if (TypeHelper.TypesAreEquivalent(iface, resolvedClosureType.PlatformType.SystemCollectionsIEnumerator)) return; } if (this.remover.sourceMethodBody.privateHelperTypesToRemove == null) this.remover.sourceMethodBody.privateHelperTypesToRemove = new List<ITypeDefinition>(); this.remover.sourceMethodBody.privateHelperTypesToRemove.Add(resolvedClosureType); if (locDef != null) this.remover.currentClosureLocals.Add(locDef, true); else { if (this.remover.sourceMethodBody.privateHelperFieldsToRemove == null) this.remover.sourceMethodBody.privateHelperFieldsToRemove = new Dictionary<IFieldDefinition, IFieldDefinition>(); var field = UnspecializedMethods.UnspecializedFieldDefinition(fieldRef.ResolvedField); this.remover.sourceMethodBody.privateHelperFieldsToRemove.Add(field, field); } if (resolvedClosureType.IsGeneric && this.remover.sourceMethodBody.MethodDefinition.IsGeneric) this.remover.genericParameterMapper = new GenericMethodParameterMapper(this.remover.host, this.remover.sourceMethodBody.MethodDefinition, resolvedClosureType); statements.RemoveAt(i - 1); // Walk the rest of the statements in the block (but *without* recursively // descending into them) looking for assignments that save local state into // fields in the closure. Such assignments do not belong in the method body. // // They were introduced by the compiler because the closure reads their value. // That is, such assignments are of the form: // closureLocal.f := e // where "e" is either "this", a local, a parameter, (corner case) a value not held in a local of the original program, // or another closureLocal (because sometimes the compiler generates code so // that one closure class has access to another one). // When the RHS expression is a local/parameter, then rely on a naming // convention that the field f has the same name as the local/parameter. // If it does not follow the naming convention, then the statement corresponds to // a real statement that was in the original method body. // // For each such assignment statement, delete it from the list of statements and // add "e" to the remover's table as the expression to replace all occurrences of // "closureLocal.f" throughout the method body. // // [Note on corner case: this seems to arise when a value occurs in an anonymous delegate that // isn't used outside of the anonymous delegate. // For instance: { ... var x = new Object(); M((args for lambda) => ... body of lambda contains a reference to x ...) ... } // where there are no occurrences of x in the rest of the method body. The compiler plays it safe and still treats x as a // captured local.] // for (int j = i - 1; j < statements.Count; j++) { if (statements[j] is IEmptyStatement) continue; IExpressionStatement/*?*/ es = statements[j] as IExpressionStatement; if (es == null) continue; IAssignment/*?*/ assignment = es.Expression as IAssignment; if (assignment == null) continue; IFieldReference/*?*/ closureField = assignment.Target.Definition as IFieldReference; if (closureField == null) { // check to see if it is of the form "loc := closureLocal". // I.e., a local has been introduced that is an alias for a local containing a closure instance. var targetLoc = this.TargetExpressionAsLocal(assignment.Target); var sourceLoc = this.ExpressionAsLocal(assignment.Source); if (targetLoc != null && sourceLoc != null && this.remover.currentClosureLocals.ContainsKey(sourceLoc)) { this.remover.currentClosureLocals.Add(targetLoc, true); statements.RemoveAt(j--); } continue; } var unspecializedClosureField = UnspecializedMethods.UnspecializedFieldReference(closureField); var closureFieldContainingType = UnspecializedMethods.AsUnspecializedNestedTypeReference(closureField.ContainingType); if (closureFieldContainingType == null) continue; if (!TypeHelper.TypesAreEquivalent(closureFieldContainingType, closureType)) continue; if (this.remover.capturedBinding.ContainsKey(unspecializedClosureField.InternedKey)) continue; var thisReference = assignment.Source as IThisReference; if (thisReference == null) { var/*?*/ binding = assignment.Source as IBoundExpression; //if (binding == null) { // //The closure is capturing a local that is defined in the block being closed over. Need to introduce the local. // var newLocal = new LocalDefinition() { // Name = closureField.Name, // Type = closureField.Type, // }; // var newLocalDecl = new LocalDeclarationStatement() { LocalVariable = newLocal, InitialValue = assignment.Source }; // statements[j] = newLocalDecl; // if (this.remover.sourceMethodBody.privateHelperFieldsToRemove == null) // this.remover.sourceMethodBody.privateHelperFieldsToRemove = new Dictionary<IFieldDefinition, IFieldDefinition>(); // this.remover.sourceMethodBody.privateHelperFieldsToRemove[unspecializedClosureField.ResolvedField] = unspecializedClosureField.ResolvedField; // this.remover.capturedBinding.Add(unspecializedClosureField.InternedKey, new BoundExpression() { Definition = newLocal, Type = newLocal.Type }); // continue; //} if (binding != null && (binding.Definition is IParameterDefinition || binding.Definition is ILocalDefinition)) { var p = binding.Definition as IParameterDefinition; if (p != null) { if (closureField.Name != p.Name) { continue; } else { this.remover.capturedBinding[unspecializedClosureField.InternedKey] = binding; } } else { // must be a local var l = binding.Definition as ILocalDefinition; if (closureField.Name != l.Name) { // Check to see if it is closureLocal.f := other_closure_local // If so, delete it. var sourceLoc = ExpressionAsLocal(assignment.Source); if (sourceLoc != null && (this.remover.currentClosureLocals.ContainsKey(sourceLoc) || this.exceptionContainer == sourceLoc)) { statements.RemoveAt(j--); if (this.exceptionContainer == sourceLoc) this.remover.capturedBinding[unspecializedClosureField.InternedKey] = binding; } continue; } else { this.remover.capturedBinding[unspecializedClosureField.InternedKey] = binding; } } } else if (binding != null && fieldRef != null) { //In this case the closure is inside an iterator and its closure fields get their values from iterator state class fields or expressions. //In the former case, arrange for all references to the closure field to become references to the corresponding iterator state field. //In the latter case, the loop below will introduce a local to hold the value of the expression and arrange for references to the closure //field to become a reference to the local. IFieldReference iteratorField = binding.Definition as IFieldReference; if (iteratorField != null && binding.Instance is IThisReference) { this.remover.capturedBinding[unspecializedClosureField.InternedKey] = binding; } else continue; } else if (binding != null) { //In this case the closure is inside another closure and the closure fields get their values from the fields of the outer closure. IFieldReference outerClosureField = binding.Definition as IFieldReference; if (outerClosureField != null && binding.Instance is IThisReference) { this.remover.capturedBinding[unspecializedClosureField.InternedKey] = binding; } else continue; } else { // Corner case: see note above LocalDefinition localDefinition = new LocalDefinition() { Name = closureField.ResolvedField.Name, Type = this.remover.genericParameterMapper == null ? closureField.Type : this.remover.genericParameterMapper.Visit(closureField.Type), }; LocalDeclarationStatement localDeclStatement = new LocalDeclarationStatement() { LocalVariable = localDefinition, InitialValue = assignment.Source, }; statements.Insert(j, localDeclStatement); j++; this.remover.capturedBinding[unspecializedClosureField.InternedKey] = new BoundExpression() { Definition = localDefinition }; if (this.remover.sourceMethodBody.privateHelperFieldsToRemove == null) this.remover.sourceMethodBody.privateHelperFieldsToRemove = new Dictionary<IFieldDefinition, IFieldDefinition>(); this.remover.sourceMethodBody.privateHelperFieldsToRemove[closureField.ResolvedField] = closureField.ResolvedField; } } else { this.remover.capturedBinding[unspecializedClosureField.InternedKey] = new BoundExpression() { Instance = thisReference }; } statements.RemoveAt(j--); } foreach (var field in closureType.ResolvedType.Fields) { if (this.remover.capturedBinding.ContainsKey(field.InternedKey)) continue; var newLocal = new LocalDefinition() { Name = field.Name, Type = this.remover.genericParameterMapper == null ? field.Type : this.remover.genericParameterMapper.Visit(field.Type), }; var newLocalDecl = new LocalDeclarationStatement() { LocalVariable = newLocal }; statements.Insert(i - 1, newLocalDecl); if (this.remover.sourceMethodBody.privateHelperFieldsToRemove == null) this.remover.sourceMethodBody.privateHelperFieldsToRemove = new Dictionary<IFieldDefinition, IFieldDefinition>(); this.remover.sourceMethodBody.privateHelperFieldsToRemove[field] = field; this.remover.capturedBinding.Add(field.InternedKey, new BoundExpression() { Definition = newLocal, Type = newLocal.Type }); } }
// For the first assignment to a local variable in a block before a control statement is hit, // if the local variable is not mentioned previously, we turn this assignment into a local declaration. private void AddDeclarationsWithInitialValues(IEnumerable<ILocalDefinition> localVariables, BasicBlock block) { List<ILocalDefinition> topLevelLocals = new List<ILocalDefinition>(localVariables); List<ILocalDefinition> localsMet = new List<ILocalDefinition>(); for (int i = 0; i < block.Statements.Count; i++) { if (topLevelLocals.Count == 0) break; IExpressionStatement expressionStatement = block.Statements[i] as IExpressionStatement; if (expressionStatement != null) { IAssignment assignment = expressionStatement.Expression as IAssignment; if (assignment != null) { ILocalDefinition localDef = assignment.Target.Definition as ILocalDefinition; if (localDef != null && topLevelLocals.Contains(localDef) && !localsMet.Contains(localDef) && !this.declaredLocals.ContainsKey(localDef)) { LocalDeclarationStatement localDecl = new LocalDeclarationStatement() { LocalVariable = localDef, InitialValue = assignment.Source, Locations = new List<ILocation>(expressionStatement.Locations), }; this.declaredLocals.Add(localDef, true); block.Statements[i] = localDecl; topLevelLocals.Remove(localDef); localsMet.Add(localDef); } } } LocalFinder finder = new LocalFinder(); finder.Traverse(block.Statements[i]); foreach (ILocalDefinition local in finder.FoundLocals) { if (!localsMet.Contains(local)) localsMet.Add(local); } //Once we see a statement that can transfer control somewhere else, we //no longer know that any subsequent assignment dominates all references //and hence cannot postpone adding the declaration until we can unify it with the assignment. IGotoStatement gotoStatement = block.Statements[i] as IGotoStatement; if (gotoStatement != null) break; IConditionalStatement conditionalStatement = block.Statements[i] as IConditionalStatement; if (conditionalStatement != null) break; ISwitchStatement switchStatement = block.Statements[i] as ISwitchStatement; if (switchStatement != null) break; IForEachStatement foreachStatement = block.Statements[i] as IForEachStatement; if (foreachStatement != null) break; IForStatement forStatement = block.Statements[i] as IForStatement; if (forStatement != null) break; ITryCatchFinallyStatement tryStatement = block.Statements[i] as ITryCatchFinallyStatement; if (tryStatement != null) break; } }
/// <summary> /// Visits the specified local declaration statement. /// </summary> /// <param name="localDeclarationStatement">The local declaration statement.</param> public override void Visit(ILocalDeclarationStatement localDeclarationStatement) { LocalDeclarationStatement mutableLocalDeclarationStatement = localDeclarationStatement as LocalDeclarationStatement; if (alwaysMakeACopy || mutableLocalDeclarationStatement == null) mutableLocalDeclarationStatement = new LocalDeclarationStatement(localDeclarationStatement); this.resultStatement = this.myCodeMutator.Visit(mutableLocalDeclarationStatement); }