/// <summary> /// stloc lockObj(lockExpression) /// call Enter(ldloc lockObj) /// .try BlockContainer { /// Block lockBlock (incoming: 1) { /// call WriteLine() /// leave lockBlock (nop) /// } /// /// } finally BlockContainer { /// Block exitBlock (incoming: 1) { /// call Exit(ldloc lockObj) /// leave exitBlock (nop) /// } /// /// } /// => /// .lock (lockExpression) BlockContainer { /// Block lockBlock (incoming: 1) { /// call WriteLine() /// leave lockBlock (nop) /// } /// } /// </summary> bool TransformLockMCS(Block block, int i) { if (i < 2) { return(false); } if (!(block.Instructions[i] is TryFinally body) || !(block.Instructions[i - 2] is StLoc objectStore) || !MatchCall(block.Instructions[i - 1] as Call, "Enter", objectStore.Variable)) { return(false); } if (!objectStore.Variable.IsSingleDefinition) { return(false); } if (!(body.TryBlock is BlockContainer tryContainer) || tryContainer.EntryPoint.Instructions.Count == 0 || tryContainer.EntryPoint.IncomingEdgeCount != 1) { return(false); } if (!(body.FinallyBlock is BlockContainer finallyContainer) || !MatchExitBlock(finallyContainer.EntryPoint, null, objectStore.Variable)) { return(false); } if (objectStore.Variable.LoadCount > 2) { return(false); } context.Step("LockTransformMCS", block); block.Instructions.RemoveAt(i - 1); block.Instructions.RemoveAt(i - 2); body.ReplaceWith(new LockInstruction(objectStore.Value, body.TryBlock).WithILRange(objectStore)); return(true); }
public void Run(Block block, BlockTransformContext context) { for (int i = 0; i < block.Instructions.Count; i++) { ILVariable v; ILInstruction copiedExpr; if (block.Instructions[i].MatchStLoc(out v, out copiedExpr)) { if (v.IsSingleDefinition && v.LoadCount == 0 && v.Kind == VariableKind.StackSlot) { // dead store to stack if (SemanticHelper.IsPure(copiedExpr.Flags)) { // no-op -> delete context.Step("remove dead store to stack: no-op -> delete", block.Instructions[i]); block.Instructions.RemoveAt(i--); } else { // evaluate the value for its side-effects context.Step("remove dead store to stack: evaluate the value for its side-effects", block.Instructions[i]); copiedExpr.AddILRange(block.Instructions[i]); block.Instructions[i] = copiedExpr; } } else if (v.IsSingleDefinition && CanPerformCopyPropagation(v, copiedExpr)) { DoPropagate(v, copiedExpr, block, ref i, context); } } } }
/// <summary> /// if (comp(ldsfld CachedAnonMethodDelegate == ldnull)) { /// stsfld CachedAnonMethodDelegate(DelegateConstruction) /// } /// ... one usage of CachedAnonMethodDelegate ... /// => /// ... one usage of DelegateConstruction ... /// </summary> bool CachedDelegateInitializationWithField(IfInstruction inst) { Block trueInst = inst.TrueInst as Block; if (trueInst == null || trueInst.Instructions.Count != 1 || !inst.FalseInst.MatchNop()) { return(false); } var storeInst = trueInst.Instructions[0]; if (!inst.Condition.MatchCompEquals(out ILInstruction left, out ILInstruction right) || !left.MatchLdsFld(out IField field) || !right.MatchLdNull()) { return(false); } if (!storeInst.MatchStsFld(out IField field2, out ILInstruction value) || !field.Equals(field2) || !field.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) { return(false); } if (!DelegateConstruction.IsDelegateConstruction(value as NewObj, true)) { return(false); } var nextInstruction = inst.Parent.Children.ElementAtOrDefault(inst.ChildIndex + 1); if (nextInstruction == null) { return(false); } var usages = nextInstruction.Descendants.Where(i => i.MatchLdsFld(field)).ToArray(); if (usages.Length != 1) { return(false); } context.Step("CachedDelegateInitializationWithField", inst); usages[0].ReplaceWith(value); return(true); }
/// <summary> /// stloc obj(resourceExpression) /// .try BlockContainer { /// Block IL_0003(incoming: 1) { /// call WriteLine(ldstr "using (null)") /// leave IL_0003(nop) /// } /// } finally BlockContainer { /// Block IL_0012(incoming: 1) { /// if (comp(ldloc obj != ldnull)) Block IL_001a { /// callvirt Dispose(ldnull) /// } /// leave IL_0012(nop) /// } /// } /// leave IL_0000(nop) /// => /// using (resourceExpression) { /// BlockContainer { /// Block IL_0003(incoming: 1) { /// call WriteLine(ldstr "using (null)") /// leave IL_0003(nop) /// } /// } /// } /// </summary> bool TransformUsing(Block block, int i) { if (i < 1) { return(false); } if (!(block.Instructions[i] is TryFinally tryFinally) || !(block.Instructions[i - 1] is StLoc storeInst)) { return(false); } if (!(storeInst.Value.MatchLdNull() || CheckResourceType(storeInst.Variable.Type))) { return(false); } if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(tryFinally))) { return(false); } if (storeInst.Variable.AddressInstructions.Any(la => !la.IsDescendantOf(tryFinally) || (la.IsDescendantOf(tryFinally.TryBlock) && !ILInlining.IsUsedAsThisPointerInCall(la)))) { return(false); } if (storeInst.Variable.StoreInstructions.Count > 1) { return(false); } if (!(tryFinally.FinallyBlock is BlockContainer container) || !MatchDisposeBlock(container, storeInst.Variable, storeInst.Value.MatchLdNull())) { return(false); } context.Step("UsingTransform", tryFinally); storeInst.Variable.Kind = VariableKind.UsingLocal; block.Instructions.RemoveAt(i); block.Instructions[i - 1] = new UsingInstruction(storeInst.Variable, storeInst.Value, tryFinally.TryBlock).WithILRange(storeInst); return(true); }
public void Run(Block block, BlockTransformContext context) { if (running) { throw new InvalidOperationException("LoopingBlockTransform already running. Transforms (and the CSharpDecompiler) are neither thread-safe nor re-entrant."); } running = true; try { int count = 1; do { block.ResetDirty(); block.RunTransforms(children, context); if (block.IsDirty) { context.Step($"Block is dirty; running loop iteration #{++count}.", block); } } while (block.IsDirty); } finally { running = false; } }