public void Run(ILFunction function, ILTransformContext context) { try { if (this.context != null) { throw new InvalidOperationException("Reentrancy in " + nameof(TransformDisplayClassUsage)); } this.context = context; var decompilationContext = new SimpleTypeResolveContext(context.Function.Method); // Traverse nested functions in post-order: // Inner functions are transformed before outer functions foreach (var f in function.Descendants.OfType <ILFunction>()) { foreach (var v in f.Variables.ToArray()) { if (context.Settings.YieldReturn && HandleMonoStateMachine(function, v, decompilationContext, f)) { continue; } if ((context.Settings.AnonymousMethods || context.Settings.ExpressionTrees) && IsClosure(context, v, out ITypeDefinition closureType, out var inst)) { if (!CanRemoveAllReferencesTo(context, v)) { continue; } if (inst is StObj || inst is StLoc) { instructionsToRemove.Add(inst); } AddOrUpdateDisplayClass(f, v, closureType, inst, localFunctionClosureParameter: false); continue; } if (context.Settings.LocalFunctions && f.Kind == ILFunctionKind.LocalFunction && v.Kind == VariableKind.Parameter && v.Index > -1 && f.Method.Parameters[v.Index.Value] is IParameter p && LocalFunctionDecompiler.IsClosureParameter(p, decompilationContext)) { AddOrUpdateDisplayClass(f, v, ((ByReferenceType)p.Type).ElementType.GetDefinition(), f.Body, localFunctionClosureParameter: true); continue; } AnalyzeUseSites(v); } } VisitILFunction(function); if (instructionsToRemove.Count > 0) { context.Step($"Remove instructions", function); foreach (var store in instructionsToRemove) { if (store.Parent is Block containingBlock) { containingBlock.Instructions.Remove(store); } } } foreach (var f in TreeTraversal.PostOrder(function, f => f.LocalFunctions)) { RemoveDeadVariableInit.ResetHasInitialValueFlag(f, context); } } finally { instructionsToRemove.Clear(); displayClasses.Clear(); fieldAssignmentsWithVariableValue.Clear(); this.context = null; } }
/// <summary> /// Inlines the stloc instruction at block.Instructions[pos] into the next instruction, if possible. /// </summary> public static bool InlineOneIfPossible(Block block, int pos, InliningOptions options, ILTransformContext context) { context.CancellationToken.ThrowIfCancellationRequested(); StLoc stloc = block.Instructions[pos] as StLoc; if (stloc == null || stloc.Variable.Kind == VariableKind.PinnedLocal) { return(false); } ILVariable v = stloc.Variable; // ensure the variable is accessed only a single time if (v.StoreCount != 1) { return(false); } if (v.LoadCount > 1 || v.LoadCount + v.AddressCount != 1) { return(false); } // TODO: inlining of small integer types might be semantically incorrect, // but we can't avoid it this easily without breaking lots of tests. //if (v.Type.IsSmallIntegerType()) // return false; // stloc might perform implicit truncation return(InlineOne(stloc, options, context)); }
public static bool InlineAllInBlock(ILFunction function, Block block, ILTransformContext context) { int?ctorCallStart = null; return(InlineAllInBlock(function, block, context, ref ctorCallStart)); }
void IILTransform.Run(ILFunction function, ILTransformContext context) { var callsToFix = new List <Call>(); foreach (var call in function.Descendants.OfType <Call>()) { if (!(call.Method.IsOperator && (call.Method.Name == "op_Increment" || call.Method.Name == "op_Decrement"))) { continue; } if (call.Arguments.Count != 1) { continue; } if (call.Method.DeclaringType.IsKnownType(KnownTypeCode.Decimal)) { // For decimal, legacy csc can optimize "d + 1m" to "op_Increment(d)". // We can handle these calls in ReplaceMethodCallsWithOperators. continue; } callsToFix.Add(call); } foreach (var call in callsToFix) { // A user-defined increment/decrement that was not handled by TransformAssignment. // This can happen because the variable-being-incremented was optimized out by Roslyn, // e.g. // public void Issue1552Pre(UserType a, UserType b) // { // UserType num = a + b; // Console.WriteLine(++num); // } // can end up being compiled to: // Console.WriteLine(UserType.op_Increment(a + b)); if (call.SlotInfo == StLoc.ValueSlot && call.Parent.SlotInfo == Block.InstructionSlot) { var store = (StLoc)call.Parent; var block = (Block)store.Parent; context.Step($"Fix {call.Method.Name} call at 0x{call.StartILOffset:x4} using {store.Variable.Name}", call); // stloc V(call op_Increment(...)) // -> // stloc V(...) // compound.assign op_Increment(V) call.ReplaceWith(call.Arguments[0]); block.Instructions.Insert(store.ChildIndex + 1, new UserDefinedCompoundAssign(call.Method, CompoundEvalMode.EvaluatesToNewValue, new LdLoca(store.Variable), CompoundTargetKind.Address, new LdcI4(1)).WithILRange(call)); } else { context.Step($"Fix {call.Method.Name} call at 0x{call.StartILOffset:x4} using new local", call); var newVariable = call.Arguments[0].Extract(context); if (newVariable == null) { Debug.Fail("Failed to extract argument of remaining increment/decrement"); continue; } newVariable.Type = call.GetParameter(0).Type; Debug.Assert(call.Arguments[0].MatchLdLoc(newVariable)); call.ReplaceWith(new UserDefinedCompoundAssign(call.Method, CompoundEvalMode.EvaluatesToNewValue, new LdLoca(newVariable), CompoundTargetKind.Address, new LdcI4(1)).WithILRange(call)); } } }
internal static bool IsPotentialClosure(ILTransformContext context, ITypeDefinition potentialDisplayClass) { var decompilationContext = new SimpleTypeResolveContext(context.Function.Ancestors.OfType <ILFunction>().Last().Method); return(IsPotentialClosure(decompilationContext.CurrentTypeDefinition, potentialDisplayClass)); }
private bool PatternMatchRefTypes(Block block, BlockContainer container, ILTransformContext context, ref ControlFlowGraph?cfg) { if (!block.MatchIfAtEndOfBlock(out var condition, out var trueInst, out var falseInst)) { return(false); } int pos = block.Instructions.Count - 3; if (condition.MatchLdLoc(out var conditionVar)) { // stloc conditionVar(comp.o(ldloc s == ldnull)) // if (logic.not(ldloc conditionVar)) br trueBlock if (pos < 0) { return(false); } if (!(conditionVar.IsSingleDefinition && conditionVar.LoadCount == 1 && conditionVar.Kind == VariableKind.StackSlot)) { return(false); } if (!block.Instructions[pos].MatchStLoc(conditionVar, out condition)) { return(false); } pos--; } if (condition.MatchCompEqualsNull(out var loadInNullCheck)) { ExtensionMethods.Swap(ref trueInst, ref falseInst); } else if (condition.MatchCompNotEqualsNull(out loadInNullCheck)) { // do nothing } else { return(false); } if (!loadInNullCheck.MatchLdLoc(out var s)) { return(false); } if (!s.IsSingleDefinition) { return(false); } if (s.Kind is not(VariableKind.Local or VariableKind.StackSlot)) { return(false); } if (pos < 0) { return(false); } // stloc V(isinst T(testedOperand)) ILInstruction storeToV = block.Instructions[pos]; if (!storeToV.MatchStLoc(out var v, out var value)) { return(false); } if (value.MatchLdLoc(s)) { // stloc v(ldloc s) pos--; if (pos < 0 || !block.Instructions[pos].MatchStLoc(s, out value)) { return(false); } if (v.Kind is not(VariableKind.Local or VariableKind.StackSlot)) { return(false); } if (s.LoadCount != 2) { return(false); } } else { if (v != s) { return(false); } } IType?unboxType; if (value is UnboxAny unboxAny) { // stloc S(unbox.any T(isinst T(testedOperand))) unboxType = unboxAny.Type; value = unboxAny.Argument; } else { unboxType = null; } if (value is not IsInst { Argument : var testedOperand, Type : var type })
/// <summary> /// Inlines 'expr' into 'next', if possible. /// /// Note that this method does not check whether 'v' has only one use; /// the caller is expected to validate whether inlining 'v' has any effects on other uses of 'v'. /// </summary> static bool DoInline(ILVariable v, ILInstruction inlinedExpression, ILInstruction next, InliningOptions options, ILTransformContext context) { var r = FindLoadInNext(next, v, inlinedExpression, options); if (r.Type == FindResultType.Found || r.Type == FindResultType.NamedArgument) { var loadInst = r.LoadInst; if (loadInst.OpCode == OpCode.LdLoca) { if (!IsGeneratedValueTypeTemporary((LdLoca)loadInst, v, inlinedExpression)) { return(false); } } else { Debug.Assert(loadInst.OpCode == OpCode.LdLoc); bool aggressive = (options & InliningOptions.Aggressive) != 0; if (!aggressive && v.Kind != VariableKind.StackSlot && !NonAggressiveInlineInto(next, r, inlinedExpression, v)) { return(false); } } if (r.Type == FindResultType.NamedArgument) { NamedArgumentTransform.IntroduceNamedArgument(r.CallArgument, context); // Now that the argument is evaluated early, we can inline as usual } context.Step($"Inline variable '{v.Name}'", inlinedExpression); // Assign the ranges of the ldloc instruction: inlinedExpression.AddILRange(loadInst); if (loadInst.OpCode == OpCode.LdLoca) { // it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof' // to preserve the semantics of the compiler-generated temporary Debug.Assert(((LdLoca)loadInst).Variable == v); loadInst.ReplaceWith(new AddressOf(inlinedExpression, v.Type)); } else { loadInst.ReplaceWith(inlinedExpression); } return(true); } return(false); }
public void Run(ILFunction function, ILTransformContext context) { this.context = context; Default(function); }
public void Run(ILFunction function, ILTransformContext context) { this.context = context; foreach (var inst in function.Descendants.OfType <CallInstruction>()) { MethodDefinition methodDef = context.TypeSystem.GetCecil(inst.Method) as MethodDefinition; if (methodDef != null && methodDef.Body != null) { if (inst.Method.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) { // partially copied from CSharpDecompiler var specializingTypeSystem = this.context.TypeSystem.GetSpecializingTypeSystem(inst.Method.Substitution); var ilReader = new ILReader(specializingTypeSystem); System.Threading.CancellationToken cancellationToken = new System.Threading.CancellationToken(); var proxyFunction = ilReader.ReadIL(methodDef.Body, cancellationToken); var transformContext = new ILTransformContext(proxyFunction, specializingTypeSystem, this.context.Settings) { CancellationToken = cancellationToken, DecompileRun = context.DecompileRun }; foreach (var transform in CSharp.CSharpDecompiler.GetILTransforms()) { if (transform.GetType() != typeof(ProxyCallReplacer)) // don't call itself on itself { cancellationToken.ThrowIfCancellationRequested(); transform.Run(proxyFunction, transformContext); } } if (!(proxyFunction.Body is BlockContainer blockContainer)) { return; } if (blockContainer.Blocks.Count != 1) { return; } var block = blockContainer.Blocks[0]; Call call = null; if (block.Instructions.Count == 1) { // leave IL_0000 (call Test(ldloc this, ldloc A_1)) if (!block.Instructions[0].MatchLeave(blockContainer, out ILInstruction returnValue)) { return; } call = returnValue as Call; } else if (block.Instructions.Count == 2) { // call Test(ldloc this, ldloc A_1) // leave IL_0000(nop) call = block.Instructions[0] as Call; if (!block.Instructions[1].MatchLeave(blockContainer, out ILInstruction returnValue)) { return; } if (!returnValue.MatchNop()) { return; } } if (call == null) { return; } if (call.Method.IsConstructor) { return; } // check if original arguments are only correct ldloc calls for (int i = 0; i < call.Arguments.Count; i++) { var originalArg = call.Arguments[i]; if (!originalArg.MatchLdLoc(out ILVariable var) || var.Kind != VariableKind.Parameter || var.Index != i - 1) { return; } } Call newInst = (Call)call.Clone(); newInst.Arguments.ReplaceList(inst.Arguments); inst.ReplaceWith(newInst); } } } }
internal static bool IsPotentialClosure(ILTransformContext context, NewObj inst) { var decompilationContext = new SimpleTypeResolveContext(context.TypeSystem.Resolve(context.Function.Method)); return(IsPotentialClosure(decompilationContext.CurrentTypeDefinition, inst.Method.DeclaringTypeDefinition)); }
public BlockTransformContext(ILTransformContext context) : base(context) { }
/// <summary> /// Inlines 'expr' into 'next', if possible. /// /// Note that this method does not check whether 'v' has only one use; /// the caller is expected to validate whether inlining 'v' has any effects on other uses of 'v'. /// </summary> static bool DoInline(ILVariable v, ILInstruction inlinedExpression, ILInstruction next, bool aggressive, ILTransformContext context) { ILInstruction loadInst; if (FindLoadInNext(next, v, inlinedExpression, out loadInst) == true) { if (loadInst.OpCode == OpCode.LdLoca) { if (!IsGeneratedValueTypeTemporary(next, loadInst.Parent, loadInst.ChildIndex, v, inlinedExpression)) { return(false); } } else { Debug.Assert(loadInst.OpCode == OpCode.LdLoc); if (!aggressive && v.Kind != VariableKind.StackSlot && !NonAggressiveInlineInto(next, loadInst, inlinedExpression, v)) { return(false); } } context.Step($"Inline variable '{v.Name}'", inlinedExpression); // Assign the ranges of the ldloc instruction: inlinedExpression.AddILRange(loadInst.ILRange); if (loadInst.OpCode == OpCode.LdLoca) { // it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof' // to preserve the semantics of the compiler-generated temporary loadInst.ReplaceWith(new AddressOf(inlinedExpression)); } else { loadInst.ReplaceWith(inlinedExpression); } return(true); } return(false); }
/// <summary> /// Inlines the stloc instruction at block.Instructions[pos] into the next instruction, if possible. /// </summary> public static bool InlineOneIfPossible(Block block, int pos, bool aggressive, ILTransformContext context) { context.CancellationToken.ThrowIfCancellationRequested(); StLoc stloc = block.Instructions[pos] as StLoc; if (stloc == null || stloc.Variable.Kind == VariableKind.PinnedLocal) { return(false); } ILVariable v = stloc.Variable; // ensure the variable is accessed only a single time if (v.StoreCount != 1) { return(false); } if (v.LoadCount > 1 || v.LoadCount + v.AddressCount != 1) { return(false); } return(InlineOne(stloc, aggressive, context)); }
void Run(CallInstruction inst, ILTransformContext context) { if (inst.Method.IsStatic) { return; } if (inst.Method.MetadataToken.IsNil || inst.Method.MetadataToken.Kind != HandleKind.MethodDefinition) { return; } var handle = (MethodDefinitionHandle)inst.Method.MetadataToken; if (!IsDefinedInCurrentOrOuterClass(inst.Method, context.Function.Method.DeclaringTypeDefinition)) { return; } if (!inst.Method.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) { return; } var metadata = context.PEFile.Metadata; MethodDefinition methodDef = metadata.GetMethodDefinition((MethodDefinitionHandle)inst.Method.MetadataToken); if (!methodDef.HasBody()) { return; } var genericContext = DelegateConstruction.GenericContextFromTypeArguments(inst.Method.Substitution); if (genericContext == null) { return; } // partially copied from CSharpDecompiler var ilReader = context.CreateILReader(); var body = context.PEFile.Reader.GetMethodBody(methodDef.RelativeVirtualAddress); var proxyFunction = ilReader.ReadIL(handle, body, genericContext.Value, context.CancellationToken); var transformContext = new ILTransformContext(context, proxyFunction); proxyFunction.RunTransforms(CSharp.CSharpDecompiler.EarlyILTransforms(), transformContext); if (!(proxyFunction.Body is BlockContainer blockContainer)) { return; } if (blockContainer.Blocks.Count != 1) { return; } var block = blockContainer.Blocks[0]; Call call; ILInstruction returnValue; switch (block.Instructions.Count) { case 1: // leave IL_0000 (call Test(ldloc this, ldloc A_1)) if (!block.Instructions[0].MatchLeave(blockContainer, out returnValue)) { return; } call = returnValue as Call; break; case 2: // call Test(ldloc this, ldloc A_1) // leave IL_0000(nop) call = block.Instructions[0] as Call; if (!block.Instructions[1].MatchLeave(blockContainer, out returnValue)) { return; } if (!returnValue.MatchNop()) { return; } break; default: return; } if (call == null || call.Method.IsConstructor) { return; } if (call.Method.IsStatic || call.Method.Parameters.Count != inst.Method.Parameters.Count) { return; } // check if original arguments are only correct ldloc calls for (int i = 0; i < call.Arguments.Count; i++) { var originalArg = call.Arguments[i]; if (!originalArg.MatchLdLoc(out ILVariable var) || var.Kind != VariableKind.Parameter || var.Index != i - 1) { return; } } context.Step("Replace proxy: " + inst.Method.Name + " with " + call.Method.Name, inst); Call newInst = (Call)call.Clone(); newInst.Arguments.ReplaceList(inst.Arguments); inst.ReplaceWith(newInst); }
public NullPropagationTransform(ILTransformContext context) { this.context = context; }
static void DoPropagate(ILVariable v, ILInstruction copiedExpr, Block block, ref int i, ILTransformContext context) { context.Step($"Copy propagate {v.Name}", copiedExpr); // un-inline the arguments of the ldArg instruction ILVariable[] uninlinedArgs = new ILVariable[copiedExpr.Children.Count]; for (int j = 0; j < uninlinedArgs.Length; j++) { var arg = copiedExpr.Children[j]; var type = context.TypeSystem.FindType(arg.ResultType); uninlinedArgs[j] = new ILVariable(VariableKind.StackSlot, type, arg.ResultType) { Name = "C_" + arg.StartILOffset, HasGeneratedName = true, }; block.Instructions.Insert(i++, new StLoc(uninlinedArgs[j], arg)); } v.Function.Variables.AddRange(uninlinedArgs); // perform copy propagation: foreach (var expr in v.LoadInstructions.ToArray()) { var clone = copiedExpr.Clone(); for (int j = 0; j < uninlinedArgs.Length; j++) { clone.Children[j].ReplaceWith(new LdLoc(uninlinedArgs[j])); } expr.ReplaceWith(clone); } block.Instructions.RemoveAt(i); int c = ILInlining.InlineInto(block, i, InliningOptions.None, context: context); i -= c + 1; }
public void Run(ILFunction function, ILTransformContext context) { if (!context.Settings.Dynamic) { return; } this.context = context; Dictionary <IField, CallSiteInfo> callsites = new Dictionary <IField, CallSiteInfo>(); HashSet <BlockContainer> modifiedContainers = new HashSet <BlockContainer>(); foreach (var block in function.Descendants.OfType <Block>()) { if (block.Instructions.Count < 2) { continue; } // Check if, we deal with a callsite cache field null check: // if (comp(ldsfld <>p__3 == ldnull)) br IL_000c // br IL_002b if (!(block.Instructions.SecondToLastOrDefault() is IfInstruction ifInst)) { continue; } if (!(block.Instructions.LastOrDefault() is Branch branchAfterInit)) { continue; } if (!MatchCallSiteCacheNullCheck(ifInst.Condition, out var callSiteCacheField, out var callSiteDelegate, out bool invertBranches)) { continue; } if (!ifInst.TrueInst.MatchBranch(out var trueBlock)) { continue; } Block callSiteInitBlock, targetBlockAfterInit; if (invertBranches) { callSiteInitBlock = branchAfterInit.TargetBlock; targetBlockAfterInit = trueBlock; } else { callSiteInitBlock = trueBlock; targetBlockAfterInit = branchAfterInit.TargetBlock; } if (!ScanCallSiteInitBlock(callSiteInitBlock, callSiteCacheField, callSiteDelegate, out var callSiteInfo, out var blockAfterInit)) { continue; } if (targetBlockAfterInit != blockAfterInit) { continue; } callSiteInfo.DelegateType = callSiteDelegate; callSiteInfo.ConditionalJumpToInit = ifInst; callSiteInfo.Inverted = invertBranches; callSiteInfo.BranchAfterInit = branchAfterInit; callsites.Add(callSiteCacheField, callSiteInfo); } var storesToRemove = new List <StLoc>(); foreach (var invokeCall in function.Descendants.OfType <CallVirt>()) { if (invokeCall.Method.DeclaringType.Kind != TypeKind.Delegate || invokeCall.Method.Name != "Invoke" || invokeCall.Arguments.Count == 0) { continue; } var firstArgument = invokeCall.Arguments[0]; if (firstArgument.MatchLdLoc(out var stackSlot) && stackSlot.Kind == VariableKind.StackSlot && stackSlot.IsSingleDefinition) { firstArgument = ((StLoc)stackSlot.StoreInstructions[0]).Value; } if (!firstArgument.MatchLdFld(out var cacheFieldLoad, out var targetField)) { continue; } if (!cacheFieldLoad.MatchLdsFld(out var cacheField)) { continue; } if (!callsites.TryGetValue(cacheField, out var callsite)) { continue; } context.Stepper.Step("Transform callsite for " + callsite.MemberName); var deadArguments = new List <ILInstruction>(); ILInstruction replacement = MakeDynamicInstruction(callsite, invokeCall, deadArguments); if (replacement == null) { continue; } invokeCall.ReplaceWith(replacement); Debug.Assert(callsite.ConditionalJumpToInit?.Parent is Block); var block = ((Block)callsite.ConditionalJumpToInit.Parent); if (callsite.Inverted) { block.Instructions.Remove(callsite.ConditionalJumpToInit); callsite.BranchAfterInit.ReplaceWith(callsite.ConditionalJumpToInit.TrueInst); } else { block.Instructions.Remove(callsite.ConditionalJumpToInit); } foreach (var arg in deadArguments) { if (arg.MatchLdLoc(out var temporary) && temporary.Kind == VariableKind.StackSlot && temporary.IsSingleDefinition && temporary.LoadCount == 0) { StLoc stLoc = (StLoc)temporary.StoreInstructions[0]; if (stLoc.Parent is Block storeParentBlock) { var value = stLoc.Value; if (value.MatchLdsFld(out var cacheFieldCopy) && cacheFieldCopy.Equals(cacheField)) { storesToRemove.Add(stLoc); } if (value.MatchLdFld(out cacheFieldLoad, out var targetFieldCopy) && cacheFieldLoad.MatchLdsFld(out cacheFieldCopy) && cacheField.Equals(cacheFieldCopy) && targetField.Equals(targetFieldCopy)) { storesToRemove.Add(stLoc); } } } } modifiedContainers.Add((BlockContainer)block.Parent); } foreach (var inst in storesToRemove) { Block parentBlock = (Block)inst.Parent; parentBlock.Instructions.RemoveAt(inst.ChildIndex); } foreach (var container in modifiedContainers) { container.SortBlocks(deleteUnreachableBlocks: true); } }
ILFunction TransformDelegateConstruction( ILInstruction value, IMethod targetMethod, ILInstruction target, IType delegateType) { if (!IsAnonymousMethod(decompilationContext.CurrentTypeDefinition, targetMethod)) { return(null); } if (targetMethod.MetadataToken.IsNil) { return(null); } if (LocalFunctionDecompiler.IsLocalFunctionMethod(targetMethod, context)) { return(null); } if (!ValidateDelegateTarget(target)) { return(null); } var handle = (MethodDefinitionHandle)targetMethod.MetadataToken; if (activeMethods.Contains(handle)) { this.context.Function.Warnings.Add(" Found self-referencing delegate construction. Abort transformation to avoid stack overflow."); return(null); } var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken); if (!methodDefinition.HasBody()) { return(null); } var genericContext = GenericContextFromTypeArguments(targetMethod.Substitution); if (genericContext == null) { return(null); } var ilReader = context.CreateILReader(); var body = context.PEFile.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress); var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, ILFunctionKind.Delegate, context.CancellationToken); function.DelegateType = delegateType; // Embed the lambda into the parent function's ILAst, so that "Show steps" can show // how the lambda body is being transformed. value.ReplaceWith(function); function.CheckInvariant(ILPhase.Normal); var contextPrefix = targetMethod.Name; foreach (ILVariable v in function.Variables.Where(v => v.Kind != VariableKind.Parameter)) { v.Name = contextPrefix + v.Name; } var nestedContext = new ILTransformContext(context, function); function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is DelegateConstruction)).Concat(GetTransforms()), nestedContext); nestedContext.Step("DelegateConstruction (ReplaceDelegateTargetVisitor)", function); function.AcceptVisitor(new ReplaceDelegateTargetVisitor(target, function.Variables.SingleOrDefault(VariableKindExtensions.IsThis))); // handle nested lambdas nestedContext.StepStartGroup("DelegateConstruction (nested lambdas)", function); ((IILTransform)this).Run(function, nestedContext); nestedContext.StepEndGroup(); function.AddILRange(target); function.AddILRange(value); if (value is Call call) { function.AddILRange(call.Arguments[1]); } return(function); }
public void Run(ILFunction function, ILTransformContext context) { this.context = context; currentFieldNames = function.Method.DeclaringTypeDefinition.Fields.Select(f => f.Name).ToArray(); reservedVariableNames = new Dictionary <string, int>(); loopCounters = CollectLoopCounters(function); foreach (var f in function.Descendants.OfType <ILFunction>()) { if (f.Method != null) { if (IsSetOrEventAccessor(f.Method) && f.Method.Parameters.Count > 0) { for (int i = 0; i < f.Method.Parameters.Count - 1; i++) { AddExistingName(reservedVariableNames, f.Method.Parameters[i].Name); } var lastParameter = f.Method.Parameters.Last(); switch (f.Method.AccessorOwner) { case IProperty prop: if (f.Method.AccessorKind == MethodSemanticsAttributes.Setter) { if (prop.Parameters.Any(p => p.Name == "value")) { f.Warnings.Add("Parameter named \"value\" already present in property signature!"); break; } var variableForLastParameter = f.Variables.FirstOrDefault(v => v.Function == f && v.Kind == VariableKind.Parameter && v.Index == f.Method.Parameters.Count - 1); if (variableForLastParameter == null) { AddExistingName(reservedVariableNames, lastParameter.Name); } else { if (variableForLastParameter.Name != "value") { variableForLastParameter.Name = "value"; } AddExistingName(reservedVariableNames, variableForLastParameter.Name); } } break; case IEvent ev: if (f.Method.AccessorKind != MethodSemanticsAttributes.Raiser) { var variableForLastParameter = f.Variables.FirstOrDefault(v => v.Function == f && v.Kind == VariableKind.Parameter && v.Index == f.Method.Parameters.Count - 1); if (variableForLastParameter == null) { AddExistingName(reservedVariableNames, lastParameter.Name); } else { if (variableForLastParameter.Name != "value") { variableForLastParameter.Name = "value"; } AddExistingName(reservedVariableNames, variableForLastParameter.Name); } } break; default: AddExistingName(reservedVariableNames, lastParameter.Name); break; } } else { foreach (var p in f.Method.Parameters) { AddExistingName(reservedVariableNames, p.Name); } } } else { foreach (var p in f.Variables.Where(v => v.Kind == VariableKind.Parameter)) { AddExistingName(reservedVariableNames, p.Name); } } } foreach (ILFunction f in function.Descendants.OfType <ILFunction>().Reverse()) { PerformAssignment(f); } }
public void Run(Block block, BlockTransformContext context) { this.context = context; Default(block); }
public void Run(ILFunction function, ILTransformContext context) { throw new NotImplementedException(); }
ILFunction TransformDelegateConstruction(NewObj value, out ILInstruction target) { target = null; if (!IsDelegateConstruction(value)) { return(null); } var targetMethod = ((IInstructionWithMethodOperand)value.Arguments[1]).Method; if (!IsAnonymousMethod(decompilationContext.CurrentTypeDefinition, targetMethod)) { return(null); } if (LocalFunctionDecompiler.IsLocalFunctionMethod(targetMethod.ParentModule.PEFile, (MethodDefinitionHandle)targetMethod.MetadataToken)) { return(null); } target = value.Arguments[0]; if (targetMethod.MetadataToken.IsNil) { return(null); } var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken); if (!methodDefinition.HasBody()) { return(null); } var genericContext = GenericContextFromTypeArguments(targetMethod.Substitution); if (genericContext == null) { return(null); } var ilReader = context.CreateILReader(); var body = context.PEFile.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress); var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, context.CancellationToken); function.DelegateType = value.Method.DeclaringType; function.CheckInvariant(ILPhase.Normal); // Embed the lambda into the parent function's ILAst, so that "Show steps" can show // how the lambda body is being transformed. value.ReplaceWith(function); var contextPrefix = targetMethod.Name; foreach (ILVariable v in function.Variables.Where(v => v.Kind != VariableKind.Parameter)) { v.Name = contextPrefix + v.Name; } var nestedContext = new ILTransformContext(context, function); function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is DelegateConstruction)).Concat(GetTransforms()), nestedContext); nestedContext.Step("DelegateConstruction (ReplaceDelegateTargetVisitor)", function); function.AcceptVisitor(new ReplaceDelegateTargetVisitor(target, function.Variables.SingleOrDefault(v => v.Index == -1 && v.Kind == VariableKind.Parameter))); // handle nested lambdas nestedContext.StepStartGroup("DelegateConstruction (nested lambdas)", function); ((IILTransform) new DelegateConstruction()).Run(function, nestedContext); nestedContext.StepEndGroup(); function.AddILRange(target); function.AddILRange(value); function.AddILRange(value.Arguments[1]); return(function); }
internal static bool IsPotentialClosure(ILTransformContext context, NewObj inst) { var decompilationContext = new SimpleTypeResolveContext(context.Function.Ancestors.OfType <ILFunction>().Last().Method); return(IsPotentialClosure(decompilationContext.CurrentTypeDefinition, inst.Method.DeclaringTypeDefinition)); }
/// <summary> /// The transform works like this: /// /// <para> /// local functions can either be used in method calls, i.e., call and callvirt instructions, /// or can be used as part of the "delegate construction" pattern, i.e., <c>newobj Delegate(<target-expression>, ldftn <method>)</c>. /// </para> /// As local functions can be declared practically anywhere, we have to take a look at all use-sites and infer the declaration location from that. Use-sites can be call, callvirt and ldftn instructions. /// After all use-sites are collected we construct the ILAst of the local function and add it to the parent function. /// Then all use-sites of the local-function are transformed to a call to the <c>LocalFunctionMethod</c> or a ldftn of the <c>LocalFunctionMethod</c>. /// In a next step we handle all nested local functions. /// After all local functions are transformed, we move all local functions that capture any variables to their respective declaration scope. /// </summary> public void Run(ILFunction function, ILTransformContext context) { if (!context.Settings.LocalFunctions) { return; } this.context = context; this.resolveContext = new SimpleTypeResolveContext(function.Method); var localFunctions = new Dictionary <MethodDefinitionHandle, LocalFunctionInfo>(); var cancellationToken = context.CancellationToken; // Find all local functions declared inside this method, including nested local functions or local functions declared in lambdas. FindUseSites(function, context, localFunctions); foreach (var(method, info) in localFunctions) { cancellationToken.ThrowIfCancellationRequested(); var firstUseSite = info.UseSites[0]; context.StepStartGroup($"Transform " + info.Definition.Name, info.Definition); try { var localFunction = info.Definition; if (!localFunction.Method.IsStatic) { var target = firstUseSite.Arguments[0]; context.Step($"Replace 'this' with {target}", localFunction); var thisVar = localFunction.Variables.SingleOrDefault(VariableKindExtensions.IsThis); localFunction.AcceptVisitor(new DelegateConstruction.ReplaceDelegateTargetVisitor(target, thisVar)); } foreach (var useSite in info.UseSites) { context.Step($"Transform use site at IL_{useSite.StartILOffset:x4}", useSite); if (useSite.OpCode == OpCode.NewObj) { TransformToLocalFunctionReference(localFunction, useSite); } else { DetermineCaptureAndDeclarationScope(localFunction, useSite); TransformToLocalFunctionInvocation(localFunction.ReducedMethod, useSite); } if (function.Method.IsConstructor && localFunction.DeclarationScope == null) { localFunction.DeclarationScope = BlockContainer.FindClosestContainer(useSite); } } if (localFunction.DeclarationScope == null) { localFunction.DeclarationScope = (BlockContainer)function.Body; } else if (localFunction.DeclarationScope != function.Body && localFunction.DeclarationScope.Parent is ILFunction declaringFunction) { function.LocalFunctions.Remove(localFunction); declaringFunction.LocalFunctions.Add(localFunction); } } finally { context.StepEndGroup(); } } }
/// <summary> /// Aggressively inlines the stloc instruction at block.Body[pos] into the next instruction, if possible. /// </summary> public static bool InlineIfPossible(Block block, int pos, ILTransformContext context) { return(InlineOneIfPossible(block, pos, InliningOptions.Aggressive, context)); }
public void Run(ILFunction function, ILTransformContext context) { ResetHasInitialValueFlag(function, context); // Remove dead stores to variables that are never read from. // If the stored value has some side-effect, the value is unwrapped. // This is necessary to remove useless stores generated by some compilers, e.g., the F# compiler. // In yield return + async, the C# compiler tends to store null/default(T) to variables // when the variable goes out of scope. if (function.IsAsync || function.IsIterator || context.Settings.RemoveDeadStores) { var variableQueue = new Queue <ILVariable>(function.Variables); while (variableQueue.Count > 0) { var v = variableQueue.Dequeue(); if (v.Kind != VariableKind.Local && v.Kind != VariableKind.StackSlot) { continue; } // Skip variables that are captured in a mcs yield state-machine // loads of these will only be visible after DelegateConstruction step. if (function.StateMachineCompiledWithMono && v.StateMachineField != null) { continue; } if (v.LoadCount != 0 || v.AddressCount != 0) { continue; } foreach (var stloc in v.StoreInstructions.OfType <StLoc>().ToArray()) { if (stloc.Parent is Block block) { if (SemanticHelper.IsPure(stloc.Value.Flags)) { block.Instructions.Remove(stloc); } else { stloc.ReplaceWith(stloc.Value); } if (stloc.Value is LdLoc ldloc) { variableQueue.Enqueue(ldloc.Variable); } } } } } // Try to infer IType of stack slots that are of StackType.Ref: foreach (var v in function.Variables) { if (v.Kind == VariableKind.StackSlot && v.StackType == StackType.Ref && v.AddressCount == 0) { IType newType = null; // Multiple store are possible in case of (c ? ref a : ref b) += 1, for example. foreach (var stloc in v.StoreInstructions.OfType <StLoc>()) { var inferredType = stloc.Value.InferType(context.TypeSystem); // cancel, if types of values do not match exactly if (newType != null && !newType.Equals(inferredType)) { newType = SpecialType.UnknownType; break; } newType = inferredType; } // Only overwrite existing type, if a "better" type was found. if (newType != null && newType != SpecialType.UnknownType) { v.Type = newType; } } } }
/// <summary> /// Inlines 'expr' into 'next', if possible. /// /// Note that this method does not check whether 'v' has only one use; /// the caller is expected to validate whether inlining 'v' has any effects on other uses of 'v'. /// </summary> static bool DoInline(ILVariable v, ILInstruction inlinedExpression, ILInstruction next, InliningOptions options, ILTransformContext context) { var r = FindLoadInNext(next, v, inlinedExpression, out var loadInst); if (r == FindResult.Found) { if (loadInst.OpCode == OpCode.LdLoca) { if (!IsGeneratedValueTypeTemporary(next, (LdLoca)loadInst, v, inlinedExpression)) { return(false); } } else { Debug.Assert(loadInst.OpCode == OpCode.LdLoc); bool aggressive = (options & InliningOptions.Aggressive) != 0; if (!aggressive && v.Kind != VariableKind.StackSlot && !NonAggressiveInlineInto(next, loadInst, inlinedExpression, v)) { return(false); } } context.Step($"Inline variable '{v.Name}'", inlinedExpression); // Assign the ranges of the ldloc instruction: inlinedExpression.AddILRange(loadInst.ILRange); if (loadInst.OpCode == OpCode.LdLoca) { // it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof' // to preserve the semantics of the compiler-generated temporary loadInst.ReplaceWith(new AddressOf(inlinedExpression)); } else { loadInst.ReplaceWith(inlinedExpression); } return(true); } else if (r == FindResult.NamedArgument && (options & InliningOptions.IntroduceNamedArguments) != 0) { return(NamedArgumentTransform.DoInline(v, (StLoc)inlinedExpression.Parent, (LdLoc)loadInst, options, context)); } return(false); }
void ClearState() { displayClasses.Clear(); this.decompilationContext = null; this.context = null; }
public NullableLiftingTransform(ILTransformContext context) { this.context = context; this.nullableVars = null; }
internal static bool IsClosure(ILTransformContext context, ILVariable variable, out ITypeDefinition closureType, out ILInstruction initializer) { return(IsClosure(context, variable, instructionsToRemove: null, out closureType, out initializer)); }