Browse Source

First attempt at pinned regions.

pull/728/head
Daniel Grunwald 9 years ago
parent
commit
275a147224
  1. 11
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 6
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  3. 2
      ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
  4. 2
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  5. 164
      ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs
  6. 20
      ICSharpCode.Decompiler/IL/ILReader.cs
  7. 124
      ICSharpCode.Decompiler/IL/Instructions.cs
  8. 6
      ICSharpCode.Decompiler/IL/Instructions.tt
  9. 14
      ICSharpCode.Decompiler/IL/Instructions/Block.cs
  10. 12
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  11. 15
      ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs
  12. 32
      ICSharpCode.Decompiler/IL/Instructions/PinnedRegion.cs
  13. 8
      ICSharpCode.Decompiler/IL/Transforms/LoopingTransform.cs
  14. 2
      ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs
  15. 20
      ICSharpCode.Decompiler/Tests/TestCases/UnsafeCode.cs
  16. 2
      ICSharpCode.Decompiler/Tests/TestRunner.cs
  17. 170
      ICSharpCode.Decompiler/Tests/UnsafeCode.cs
  18. 12
      ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
  19. 4
      ILSpy/Languages/ILAstLanguage.cs

11
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -49,7 +49,8 @@ namespace ICSharpCode.Decompiler.CSharp
List<IILTransform> ilTransforms = new List<IILTransform> { List<IILTransform> ilTransforms = new List<IILTransform> {
new SplitVariables(), new SplitVariables(),
new ControlFlowSimplification(), new ControlFlowSimplification(),
//new ILInlining(), // temporary pass, just to make the ILAst easier to read while debugging loop detection new ILInlining(),
new DetectPinnedRegions(),
new LoopDetection(), new LoopDetection(),
new IntroduceExitPoints(), new IntroduceExitPoints(),
new ConditionDetection(), new ConditionDetection(),
@ -287,6 +288,7 @@ namespace ICSharpCode.Decompiler.CSharp
{ {
if (definitions == null) if (definitions == null)
throw new ArgumentNullException("definitions"); throw new ArgumentNullException("definitions");
ITypeDefinition parentTypeDef = null;
var syntaxTree = new SyntaxTree(); var syntaxTree = new SyntaxTree();
foreach (var def in definitions) { foreach (var def in definitions) {
if (def == null) if (def == null)
@ -301,31 +303,36 @@ namespace ICSharpCode.Decompiler.CSharp
if (typeDef == null) if (typeDef == null)
throw new InvalidOperationException("Could not find type definition in NR type system"); throw new InvalidOperationException("Could not find type definition in NR type system");
syntaxTree.Members.Add(DoDecompile(typeDef, new SimpleTypeResolveContext(typeDef))); syntaxTree.Members.Add(DoDecompile(typeDef, new SimpleTypeResolveContext(typeDef)));
parentTypeDef = typeDef.DeclaringTypeDefinition;
} else if (methodDefinition != null) { } else if (methodDefinition != null) {
IMethod method = typeSystem.Resolve(methodDefinition); IMethod method = typeSystem.Resolve(methodDefinition);
if (method == null) if (method == null)
throw new InvalidOperationException("Could not find method definition in NR type system"); throw new InvalidOperationException("Could not find method definition in NR type system");
syntaxTree.Members.Add(DoDecompile(methodDefinition, method, new SimpleTypeResolveContext(method))); syntaxTree.Members.Add(DoDecompile(methodDefinition, method, new SimpleTypeResolveContext(method)));
parentTypeDef = method.DeclaringTypeDefinition;
} else if (fieldDefinition != null) { } else if (fieldDefinition != null) {
IField field = typeSystem.Resolve(fieldDefinition); IField field = typeSystem.Resolve(fieldDefinition);
if (field == null) if (field == null)
throw new InvalidOperationException("Could not find field definition in NR type system"); throw new InvalidOperationException("Could not find field definition in NR type system");
syntaxTree.Members.Add(DoDecompile(fieldDefinition, field, new SimpleTypeResolveContext(field))); syntaxTree.Members.Add(DoDecompile(fieldDefinition, field, new SimpleTypeResolveContext(field)));
parentTypeDef = field.DeclaringTypeDefinition;
} else if (propertyDefinition != null) { } else if (propertyDefinition != null) {
IProperty property = typeSystem.Resolve(propertyDefinition); IProperty property = typeSystem.Resolve(propertyDefinition);
if (property == null) if (property == null)
throw new InvalidOperationException("Could not find field definition in NR type system"); throw new InvalidOperationException("Could not find field definition in NR type system");
syntaxTree.Members.Add(DoDecompile(propertyDefinition, property, new SimpleTypeResolveContext(property))); syntaxTree.Members.Add(DoDecompile(propertyDefinition, property, new SimpleTypeResolveContext(property)));
parentTypeDef = property.DeclaringTypeDefinition;
} else if (eventDefinition != null) { } else if (eventDefinition != null) {
IEvent ev = typeSystem.Resolve(eventDefinition); IEvent ev = typeSystem.Resolve(eventDefinition);
if (ev == null) if (ev == null)
throw new InvalidOperationException("Could not find field definition in NR type system"); throw new InvalidOperationException("Could not find field definition in NR type system");
syntaxTree.Members.Add(DoDecompile(eventDefinition, ev, new SimpleTypeResolveContext(ev))); syntaxTree.Members.Add(DoDecompile(eventDefinition, ev, new SimpleTypeResolveContext(ev)));
parentTypeDef = ev.DeclaringTypeDefinition;
} else { } else {
throw new NotSupportedException(def.GetType().Name); throw new NotSupportedException(def.GetType().Name);
} }
} }
RunTransforms(syntaxTree, new SimpleTypeResolveContext(typeSystem.MainAssembly)); RunTransforms(syntaxTree, parentTypeDef != null ? new SimpleTypeResolveContext(parentTypeDef) : new SimpleTypeResolveContext(typeSystem.MainAssembly));
return syntaxTree; return syntaxTree;
} }

6
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -490,8 +490,10 @@ namespace ICSharpCode.Decompiler.CSharp
var left = Translate(inst.Left); var left = Translate(inst.Left);
var right = Translate(inst.Right); var right = Translate(inst.Right);
ResolveResult rr; ResolveResult rr;
if (left.Type.IsKnownType(KnownTypeCode.IntPtr) || left.Type.IsKnownType(KnownTypeCode.UIntPtr) if (left.Type.GetStackType() == StackType.I || right.Type.GetStackType() == StackType.I) {
|| right.Type.IsKnownType(KnownTypeCode.IntPtr) || right.Type.IsKnownType(KnownTypeCode.UIntPtr)) { // IntPtr or pointers as input.
// C# doesn't allow adding IntPtr values, and adding pointer values has the wrong semantics
// (adding number of elements instead of number of bytes), so switch to long/ulong in both cases.
IType targetType; IType targetType;
if (inst.Sign == Sign.Unsigned) { if (inst.Sign == Sign.Unsigned) {
targetType = compilation.FindType(KnownTypeCode.UInt64); targetType = compilation.FindType(KnownTypeCode.UInt64);

2
ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs

@ -1107,7 +1107,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
methodDef.Attributes.MoveTo(dd.Attributes); methodDef.Attributes.MoveTo(dd.Attributes);
dd.Modifiers = methodDef.Modifiers & ~(Modifiers.Protected | Modifiers.Override); dd.Modifiers = methodDef.Modifiers & ~(Modifiers.Protected | Modifiers.Override);
dd.Body = m.Get<BlockStatement>("body").Single().Detach(); dd.Body = m.Get<BlockStatement>("body").Single().Detach();
dd.Name = currentTypeDefinition.Name; dd.Name = currentTypeDefinition?.Name;
methodDef.ReplaceWith(dd); methodDef.ReplaceWith(dd);
return dd; return dd;
} }

2
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -92,6 +92,7 @@
<Compile Include="FlowAnalysis\ReachingDefinitionsVisitor.cs" /> <Compile Include="FlowAnalysis\ReachingDefinitionsVisitor.cs" />
<Compile Include="IL\ControlFlow\ConditionDetection.cs" /> <Compile Include="IL\ControlFlow\ConditionDetection.cs" />
<Compile Include="IL\ControlFlow\ControlFlowSimplification.cs" /> <Compile Include="IL\ControlFlow\ControlFlowSimplification.cs" />
<Compile Include="IL\ControlFlow\DetectPinnedRegions.cs" />
<Compile Include="IL\ControlFlow\IntroduceExitPoints.cs" /> <Compile Include="IL\ControlFlow\IntroduceExitPoints.cs" />
<Compile Include="IL\ControlFlow\LoopDetection.cs" /> <Compile Include="IL\ControlFlow\LoopDetection.cs" />
<Compile Include="IL\Instructions.cs"> <Compile Include="IL\Instructions.cs">
@ -115,6 +116,7 @@
<Compile Include="IL\Instructions\LocalVarInstructions.cs" /> <Compile Include="IL\Instructions\LocalVarInstructions.cs" />
<Compile Include="IL\Instructions\MemoryInstructions.cs" /> <Compile Include="IL\Instructions\MemoryInstructions.cs" />
<Compile Include="IL\Instructions\PatternMatching.cs" /> <Compile Include="IL\Instructions\PatternMatching.cs" />
<Compile Include="IL\Instructions\PinnedRegion.cs" />
<Compile Include="IL\Instructions\Return.cs" /> <Compile Include="IL\Instructions\Return.cs" />
<Compile Include="IL\Instructions\SimpleInstruction.cs" /> <Compile Include="IL\Instructions\SimpleInstruction.cs" />
<Compile Include="IL\Instructions\SwitchInstruction.cs" /> <Compile Include="IL\Instructions\SwitchInstruction.cs" />

164
ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs

@ -0,0 +1,164 @@
// Copyright (c) 2016 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace ICSharpCode.Decompiler.IL.ControlFlow
{
/// <summary>
/// IL uses 'pinned locals' to prevent the GC from moving objects.
///
/// C#:
/// <code>
/// fixed (int* s = &amp;arr[index]) { use(s); use(s); }
/// </code>
///
/// <para>Gets translated into IL:</para>
/// <code>
/// pinned local P : System.Int32&amp;
///
/// stloc(P, ldelema(arr, index))
/// call use(conv ref->i(ldloc P))
/// call use(conv ref->i(ldloc P))
/// stloc(P, conv i4->u(ldc.i4 0))
/// </code>
///
/// In C#, the only way to pin something is to use a fixed block
/// (or to mess around with GCHandles).
/// But fixed blocks are scoped, so we need to detect the region affected by the pin.
/// To ensure we'll be able to collect all blocks in that region, we perform this transform
/// early, before building any other control flow constructs that aren't as critical for correctness.
///
/// This means this transform must run before LoopDetection.
/// To make our detection job easier, we must run after variable inlining.
/// </summary>
public class DetectPinnedRegions : IILTransform
{
public void Run(ILFunction function, ILTransformContext context)
{
foreach (var container in function.Descendants.OfType<BlockContainer>()) {
SplitBlocksAtWritesToPinnedLocals(container);
foreach (var block in container.Blocks)
Run(block);
container.Blocks.RemoveAll(b => b.Instructions.Count == 0); // remove dummy blocks
}
}
void SplitBlocksAtWritesToPinnedLocals(BlockContainer container)
{
for (int i = 0; i < container.Blocks.Count; i++) {
var block = container.Blocks[i];
for (int j = 0; j < block.Instructions.Count - 1; j++) {
var inst = block.Instructions[j];
ILVariable v;
if (inst.MatchStLoc(out v) && v.Kind == VariableKind.PinnedLocal && block.Instructions[j + 1].OpCode != OpCode.Branch) {
// split block after j:
var newBlock = new Block();
for (int k = j + 1; k < block.Instructions.Count; k++) {
newBlock.Instructions.Add(block.Instructions[k]);
}
newBlock.ILRange = newBlock.Instructions[0].ILRange;
block.Instructions.RemoveRange(j + 1, newBlock.Instructions.Count);
block.Instructions.Add(new Branch(newBlock));
container.Blocks.Insert(i + 1, newBlock);
}
}
}
}
void Run(Block block)
{
// After SplitBlocksAtWritesToPinnedLocals(), only the second-to-last instruction in each block
// can be a write to a pinned local.
var stLoc = block.Instructions.ElementAtOrDefault(block.Instructions.Count - 2) as StLoc;
if (stLoc == null || stLoc.Variable.Kind != VariableKind.PinnedLocal)
return;
// stLoc is a store to a pinned local.
if (IsNullOrZero(stLoc.Value))
return; // ignore unpin instructions
// stLoc is a store that starts a new pinned region
// Collect the blocks to be moved into the region:
BlockContainer sourceContainer = (BlockContainer)block.Parent;
int[] reachedEdgesPerBlock = new int[sourceContainer.Blocks.Count];
Queue<Block> workList = new Queue<Block>();
Block entryBlock = ((Branch)block.Instructions.Last()).TargetBlock;
if (entryBlock.Parent == sourceContainer) {
reachedEdgesPerBlock[entryBlock.ChildIndex]++;
workList.Enqueue(entryBlock);
while (workList.Count > 0) {
Block workItem = workList.Dequeue();
StLoc workStLoc = workItem.Instructions.ElementAtOrDefault(workItem.Instructions.Count - 2) as StLoc;
int instructionCount;
if (workStLoc != null && workStLoc.Variable == stLoc.Variable && IsNullOrZero(workStLoc.Value)) {
// found unpin instruction: only consider branches prior to that instruction
instructionCount = workStLoc.ChildIndex;
} else {
instructionCount = workItem.Instructions.Count;
}
for (int i = 0; i < instructionCount; i++) {
foreach (var branch in workItem.Instructions[i].Descendants.OfType<Branch>()) {
if (branch.TargetBlock.Parent == sourceContainer) {
Debug.Assert(branch.TargetBlock != block);
if (reachedEdgesPerBlock[branch.TargetBlock.ChildIndex]++ == 0) {
// detected first edge to that block: add block as work item
workList.Enqueue(branch.TargetBlock);
}
}
}
}
}
// Validate that all uses of a block consistently are inside or outside the pinned region.
// (we cannot do this anymore after we start moving blocks around)
for (int i = 0; i < sourceContainer.Blocks.Count; i++) {
if (reachedEdgesPerBlock[i] != 0 && reachedEdgesPerBlock[i] != sourceContainer.Blocks[i].IncomingEdgeCount) {
return;
}
}
BlockContainer body = new BlockContainer();
for (int i = 0; i < sourceContainer.Blocks.Count; i++) {
if (reachedEdgesPerBlock[i] > 0) {
body.Blocks.Add(sourceContainer.Blocks[i]); // move block into body
sourceContainer.Blocks[i] = new Block(); // replace with dummy block
// we'll delete the dummy block later
}
}
block.Instructions[block.Instructions.Count - 2] = new PinnedRegion(stLoc, body);
} else {
// we didn't find a single block to be added to the pinned region
block.Instructions[block.Instructions.Count - 2] = new PinnedRegion(stLoc, new Nop());
}
block.Instructions.RemoveAt(block.Instructions.Count - 1);
}
static bool IsNullOrZero(ILInstruction inst)
{
var conv = inst as Conv;
if (conv != null) {
inst = conv.Argument;
}
return inst.MatchLdcI4(0) || inst.MatchLdNull();
}
}
}

20
ICSharpCode.Decompiler/IL/ILReader.cs

@ -1071,19 +1071,27 @@ namespace ICSharpCode.Decompiler.IL
switch (condition.ResultType) { switch (condition.ResultType) {
case StackType.O: case StackType.O:
// introduce explicit comparison with null // introduce explicit comparison with null
condition = new Comp(ComparisonKind.Inequality, Sign.None, condition, new LdNull()); condition = new Comp(
negate ? ComparisonKind.Equality : ComparisonKind.Inequality,
Sign.None, condition, new LdNull());
break; break;
case StackType.I: case StackType.I:
// introduce explicit comparison with 0 // introduce explicit comparison with 0
condition = new Comp(ComparisonKind.Inequality, Sign.None, condition, new LdcI4(0)); condition = new Comp(
negate ? ComparisonKind.Equality : ComparisonKind.Inequality,
Sign.None, condition, new LdcI4(0));
break; break;
case StackType.I8: case StackType.I8:
// introduce explicit comparison with 0 // introduce explicit comparison with 0
condition = new Comp(ComparisonKind.Inequality, Sign.None, condition, new LdcI8(0)); condition = new Comp(
negate ? ComparisonKind.Equality : ComparisonKind.Inequality,
Sign.None, condition, new LdcI8(0));
break;
default:
if (negate) {
condition = new LogicNot(condition);
}
break; break;
}
if (negate) {
condition = new LogicNot(condition);
} }
MarkBranchTarget(target); MarkBranchTarget(target);
return new IfInstruction(condition, new Branch(target)); return new IfInstruction(condition, new Branch(target));

124
ICSharpCode.Decompiler/IL/Instructions.cs

@ -39,6 +39,8 @@ namespace ICSharpCode.Decompiler.IL
BlockContainer, BlockContainer,
/// <summary>A block of IL instructions.</summary> /// <summary>A block of IL instructions.</summary>
Block, Block,
/// <summary>A region where a pinned variable is used (initial representation of future fixed statement)</summary>
PinnedRegion,
/// <summary>Unary operator that expects an input of type I4. Returns 1 (of type I4) if the input value is 0. Otherwise, returns 0 (of type I4).</summary> /// <summary>Unary operator that expects an input of type I4. Returns 1 (of type I4) if the input value is 0. Otherwise, returns 0 (of type I4).</summary>
LogicNot, LogicNot,
/// <summary>Adds two numbers.</summary> /// <summary>Adds two numbers.</summary>
@ -574,6 +576,107 @@ namespace ICSharpCode.Decompiler.IL
} }
} }
/// <summary>A region where a pinned variable is used (initial representation of future fixed statement)</summary>
public sealed partial class PinnedRegion : ILInstruction
{
public PinnedRegion(ILInstruction pin, ILInstruction body) : base(OpCode.PinnedRegion)
{
this.Pin = pin;
this.Body = body;
}
public override StackType ResultType { get { return StackType.Void; } }
public static readonly SlotInfo PinSlot = new SlotInfo("Pin", canInlineInto: true);
ILInstruction pin;
public ILInstruction Pin {
get { return this.pin; }
set {
ValidateChild(value);
SetChildInstruction(ref this.pin, value, 0);
}
}
public static readonly SlotInfo BodySlot = new SlotInfo("Body");
ILInstruction body;
public ILInstruction Body {
get { return this.body; }
set {
ValidateChild(value);
SetChildInstruction(ref this.body, value, 1);
}
}
protected sealed override int GetChildCount()
{
return 2;
}
protected sealed override ILInstruction GetChild(int index)
{
switch (index) {
case 0:
return this.pin;
case 1:
return this.body;
default:
throw new IndexOutOfRangeException();
}
}
protected sealed override void SetChild(int index, ILInstruction value)
{
switch (index) {
case 0:
this.Pin = value;
break;
case 1:
this.Body = value;
break;
default:
throw new IndexOutOfRangeException();
}
}
protected sealed override SlotInfo GetChildSlot(int index)
{
switch (index) {
case 0:
return PinSlot;
case 1:
return BodySlot;
default:
throw new IndexOutOfRangeException();
}
}
public sealed override ILInstruction Clone()
{
var clone = (PinnedRegion)ShallowClone();
clone.Pin = this.pin.Clone();
clone.Body = this.body.Clone();
return clone;
}
protected override InstructionFlags ComputeFlags()
{
return pin.Flags | body.Flags;
}
public override InstructionFlags DirectFlags {
get {
return InstructionFlags.None;
}
}
public override void WriteTo(ITextOutput output)
{
output.Write(OpCode);
output.Write('(');
this.pin.WriteTo(output);
output.Write(", ");
this.body.WriteTo(output);
output.Write(')');
}
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitPinnedRegion(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitPinnedRegion(this);
}
}
/// <summary>Unary operator that expects an input of type I4. Returns 1 (of type I4) if the input value is 0. Otherwise, returns 0 (of type I4).</summary> /// <summary>Unary operator that expects an input of type I4. Returns 1 (of type I4) if the input value is 0. Otherwise, returns 0 (of type I4).</summary>
public sealed partial class LogicNot : UnaryInstruction public sealed partial class LogicNot : UnaryInstruction
{ {
@ -3116,6 +3219,10 @@ namespace ICSharpCode.Decompiler.IL
{ {
Default(block); Default(block);
} }
protected internal virtual void VisitPinnedRegion(PinnedRegion inst)
{
Default(inst);
}
protected internal virtual void VisitLogicNot(LogicNot inst) protected internal virtual void VisitLogicNot(LogicNot inst)
{ {
Default(inst); Default(inst);
@ -3418,6 +3525,10 @@ namespace ICSharpCode.Decompiler.IL
{ {
return Default(block); return Default(block);
} }
protected internal virtual T VisitPinnedRegion(PinnedRegion inst)
{
return Default(inst);
}
protected internal virtual T VisitLogicNot(LogicNot inst) protected internal virtual T VisitLogicNot(LogicNot inst)
{ {
return Default(inst); return Default(inst);
@ -3742,6 +3853,7 @@ namespace ICSharpCode.Decompiler.IL
"ILFunction", "ILFunction",
"BlockContainer", "BlockContainer",
"Block", "Block",
"PinnedRegion",
"logic.not", "logic.not",
"add", "add",
"sub", "sub",
@ -3831,6 +3943,18 @@ namespace ICSharpCode.Decompiler.IL
} }
return false; return false;
} }
public bool MatchPinnedRegion(out ILInstruction pin, out ILInstruction body)
{
var inst = this as PinnedRegion;
if (inst != null) {
pin = inst.Pin;
body = inst.Body;
return true;
}
pin = default(ILInstruction);
body = default(ILInstruction);
return false;
}
public bool MatchLogicNot(out ILInstruction argument) public bool MatchLogicNot(out ILInstruction argument)
{ {
var inst = this as LogicNot; var inst = this as LogicNot;

6
ICSharpCode.Decompiler/IL/Instructions.tt

@ -49,6 +49,12 @@
VoidResult, CustomConstructor, CustomVariableName("container")), VoidResult, CustomConstructor, CustomVariableName("container")),
new OpCode("Block", "A block of IL instructions.", new OpCode("Block", "A block of IL instructions.",
CustomConstructor, CustomVariableName("block")), CustomConstructor, CustomVariableName("block")),
new OpCode("PinnedRegion", "A region where a pinned variable is used (initial representation of future fixed statement)",
ResultType("Void"),
CustomChildren(new []{
new ChildInfo("pin") { CanInlineInto = true },
new ChildInfo("body")
})),
new OpCode("logic.not", "Unary operator that expects an input of type I4. Returns 1 (of type I4) if the input value is 0. Otherwise, returns 0 (of type I4).", new OpCode("logic.not", "Unary operator that expects an input of type I4. Returns 1 (of type I4) if the input value is 0. Otherwise, returns 0 (of type I4).",
ResultType("I4"), Unary), ResultType("I4"), Unary),
new OpCode("add", "Adds two numbers.", BinaryNumeric), new OpCode("add", "Adds two numbers.", BinaryNumeric),

14
ICSharpCode.Decompiler/IL/Instructions/Block.cs

@ -36,10 +36,11 @@ namespace ICSharpCode.Decompiler.IL
/// the FinalInstruction (which is not part of the list) will be executed. /// the FinalInstruction (which is not part of the list) will be executed.
/// The block returns returns the result value of the FinalInstruction. /// The block returns returns the result value of the FinalInstruction.
/// For blocks returning void, the FinalInstruction will usually be 'nop'. /// For blocks returning void, the FinalInstruction will usually be 'nop'.
///
/// TODO: now that 'implicit push' is gone, reconsider whether we need
/// a separate slot for FinalInstruction
/// </para> /// </para>
///
/// TODO: consider splitting inline blocks (with FinalInstruction) from those
/// used in containers for control flow purposes -- these are very different things
/// which should not share a class.
/// </summary> /// </summary>
partial class Block : ILInstruction partial class Block : ILInstruction
{ {
@ -59,6 +60,13 @@ namespace ICSharpCode.Decompiler.IL
/// </remarks> /// </remarks>
public int IncomingEdgeCount { get; internal set; } public int IncomingEdgeCount { get; internal set; }
/// <summary>
/// A 'final instruction' that gets executed after the <c>Instructions</c> collection.
/// Provides the return value for the block.
/// </summary>
/// <remarks>
/// Blocks in containers must have 'Nop' as a final instruction.
/// </remarks>
public ILInstruction FinalInstruction { public ILInstruction FinalInstruction {
get { get {
return finalInstruction; return finalInstruction;

12
ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs

@ -69,5 +69,17 @@ namespace ICSharpCode.Decompiler.IL
return InstructionFlags.MayThrow | InstructionFlags.ControlFlow; return InstructionFlags.MayThrow | InstructionFlags.ControlFlow;
} }
} }
/// <summary>
/// Apply a list of transforms to this function.
/// </summary>
public void RunTransforms(IEnumerable<IILTransform> transforms, ILTransformContext context)
{
foreach (var transform in transforms) {
context.CancellationToken.ThrowIfCancellationRequested();
transform.Run(this, context);
this.CheckInvariant(ILPhase.Normal);
}
}
} }
} }

15
ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs

@ -267,6 +267,16 @@ namespace ICSharpCode.Decompiler.IL
} }
return false; return false;
} }
public void RemoveRange(int index, int count)
{
parentInstruction.AssertNoEnumerators();
for (int i = 0; i < count; i++) {
parentInstruction.InstructionCollectionRemoved(list[index + i]);
}
list.RemoveRange(index, count);
parentInstruction.InstructionCollectionUpdateComplete();
}
/// <summary> /// <summary>
/// Removes all elements for which the predicate returns true. /// Removes all elements for which the predicate returns true.
@ -310,6 +320,11 @@ namespace ICSharpCode.Decompiler.IL
return list[list.Count - 1]; return list[list.Count - 1];
} }
public T LastOrDefault()
{
return list.Count > 0 ? list[list.Count - 1] : null;
}
public T ElementAtOrDefault(int index) public T ElementAtOrDefault(int index)
{ {
if (index >= 0 && index < list.Count) if (index >= 0 && index < list.Count)

32
ICSharpCode.Decompiler/IL/Instructions/PinnedRegion.cs

@ -0,0 +1,32 @@
// Copyright (c) 2016 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Diagnostics;
namespace ICSharpCode.Decompiler.IL
{
partial class PinnedRegion
{
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(((StLoc)this.pin).Variable.Kind == VariableKind.PinnedLocal);
}
}
}

8
ICSharpCode.Decompiler/IL/Transforms/LoopingTransform.cs

@ -31,18 +31,14 @@ namespace ICSharpCode.Decompiler.IL
public LoopingTransform(params IILTransform[] children) public LoopingTransform(params IILTransform[] children)
{ {
this.children = children.ToList(); this.children = children;
} }
public void Run(ILFunction function, ILTransformContext context) public void Run(ILFunction function, ILTransformContext context)
{ {
do { do {
function.ResetDirty(); function.ResetDirty();
foreach (var transform in children) { function.RunTransforms(children, context);
context.CancellationToken.ThrowIfCancellationRequested();
transform.Run(function, context);
function.CheckInvariant(ILPhase.Normal);
}
} while (function.IsDirty); } while (function.IsDirty);
} }

2
ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs

@ -52,7 +52,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
default: default:
// parameters: avoid splitting parameters // parameters: avoid splitting parameters
// stack slots: are already split by construction // stack slots: are already split by construction
// pinned locals: not sure if splitting those would be legal // pinned locals: must not split (doing so would extend the life of the pin to the end of the method)
return false; return false;
} }
} }

20
ICSharpCode.Decompiler/Tests/TestCases/UnsafeCode.cs

@ -23,7 +23,7 @@ public class UnsafeCode
static void Main() static void Main()
{ {
// TODO: test behavior, or convert this into a pretty-test // TODO: test behavior, or convert this into a pretty-test
// (but for now, it's already valuable knowing that the decompiled code can be re-compiled) // (but for now, it's already valuable knowing whether the decompiled code can be re-compiled)
} }
public unsafe int* NullPointer public unsafe int* NullPointer
@ -72,7 +72,25 @@ public class UnsafeCode
fixed (double* ptr = &matrix[1, 2]) fixed (double* ptr = &matrix[1, 2])
{ {
this.PointerReferenceExpression(ptr); this.PointerReferenceExpression(ptr);
this.PointerReferenceExpression(ptr);
}
}
public unsafe int MultipleExitsOutOfFixedBlock(int[] arr)
{
fixed (int* ptr = &arr[0])
{
if (*ptr < 0)
return *ptr;
if (*ptr == 21)
return 42;
if (*ptr == 42)
goto outside;
} }
return 1;
outside:
Console.WriteLine("outside");
return 2;
} }
public unsafe void FixedStringAccess(string text) public unsafe void FixedStringAccess(string text)

2
ICSharpCode.Decompiler/Tests/TestRunner.cs

@ -114,7 +114,7 @@ namespace ICSharpCode.Decompiler.Tests
TestAssembleDecompileCompileOutput("ILTest.il"); TestAssembleDecompileCompileOutput("ILTest.il");
} }
[Test, Ignore("Fixed statements and pointer arithmetic are broken")] [Test, Ignore("Fixed statements are broken")]
public void UnsafeCode() public void UnsafeCode()
{ {
TestCompileDecompileCompileOutputAll("UnsafeCode.cs"); TestCompileDecompileCompileOutputAll("UnsafeCode.cs");

170
ICSharpCode.Decompiler/Tests/UnsafeCode.cs

@ -1,170 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
public class UnsafeCode
{
public unsafe int* NullPointer
{
get
{
return null;
}
}
public unsafe long ConvertDoubleToLong(double d)
{
return *(long*)(&d);
}
public unsafe double ConvertLongToDouble(long d)
{
return *(double*)(&d);
}
public unsafe int ConvertFloatToInt(float d)
{
return *(int*)(&d);
}
public unsafe float ConvertIntToFloat(int d)
{
return *(float*)(&d);
}
public unsafe void PassRefParameterAsPointer(ref int p)
{
fixed (int* ptr = &p)
{
this.PassPointerAsRefParameter(ptr);
}
}
public unsafe void PassPointerAsRefParameter(int* p)
{
this.PassRefParameterAsPointer(ref *p);
}
public unsafe void AddressInMultiDimensionalArray(double[,] matrix)
{
fixed (double* ptr = &matrix[1, 2])
{
this.PointerReferenceExpression(ptr);
}
}
public unsafe void FixedStringAccess(string text)
{
fixed (char* ptr = text)
{
char* ptr2 = ptr;
while (*ptr2 != '\0')
{
*ptr2 = 'A';
ptr2++;
}
}
}
public unsafe void PutDoubleIntoLongArray1(long[] array, int index, double val)
{
fixed (long* ptr = array)
{
((double*)ptr)[index] = val;
}
}
public unsafe void PutDoubleIntoLongArray2(long[] array, int index, double val)
{
fixed (long* ptr = &array[index])
{
*(double*)ptr = val;
}
}
public unsafe string PointerReferenceExpression(double* d)
{
return d->ToString();
}
public unsafe void FixMultipleStrings(string text)
{
fixed (char* ptr = text, userName = Environment.UserName, ptr2 = text)
{
*ptr = 'c';
*userName = 'd';
*ptr2 = 'e';
}
}
public unsafe string StackAlloc(int count)
{
char* ptr = stackalloc char[count];
for (int i = 0; i < count; i++)
{
ptr[i] = (char)i;
}
return this.PointerReferenceExpression((double*)ptr);
}
public unsafe int* PointerArithmetic(int* p)
{
return p + 2;
}
public unsafe byte* PointerArithmetic2(long* p, int y, int x)
{
return (byte*)((short*)p + (y * x));
}
public unsafe long* PointerArithmetic3(long* p)
{
return (long*)((byte*)p + 3);
}
public unsafe long* PointerArithmetic4(void* p)
{
return (long*)((byte*)p + 3);
}
public unsafe int PointerArithmetic5(void* p, byte* q, int i)
{
return (int)(q[i] + *(byte*)p);
}
public unsafe int PointerSubtraction(long* p, long* q)
{
return (int)((long)(p - q));
}
public unsafe int PointerSubtraction2(long* p, short* q)
{
return (int)((long)((byte*)p - (byte*)q));
}
public unsafe int PointerSubtraction3(void* p, void* q)
{
return (int)((long)((byte*)p - (byte*)q));
}
unsafe ~UnsafeCode()
{
this.PassPointerAsRefParameter(this.NullPointer);
}
}

12
ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs

@ -120,16 +120,12 @@ namespace ICSharpCode.Decompiler
{ {
if (typeReference == null) if (typeReference == null)
return SpecialType.UnknownType; return SpecialType.UnknownType;
if (typeReference is SentinelType) { if (typeReference is SentinelType || typeReference is PinnedType) {
typeReference = ((SentinelType)typeReference).ElementType; typeReference = ((TypeSpecification)typeReference).ElementType;
} }
ITypeReference typeRef; ITypeReference typeRef;
if (typeReference is PinnedType) { lock (typeReferenceCecilLoader)
typeRef = new NRefactory.TypeSystem.ByReferenceType(Resolve(((PinnedType)typeReference).ElementType)).ToTypeReference(); typeRef = typeReferenceCecilLoader.ReadTypeReference(typeReference);
} else {
lock (typeReferenceCecilLoader)
typeRef = typeReferenceCecilLoader.ReadTypeReference(typeReference);
}
return typeRef.Resolve(context); return typeRef.Resolve(context);
} }
#endregion #endregion

4
ILSpy/Languages/ILAstLanguage.cs

@ -154,9 +154,7 @@ namespace ICSharpCode.ILSpy
var typeSystem = new DecompilerTypeSystem(method.Module); var typeSystem = new DecompilerTypeSystem(method.Module);
ILReader reader = new ILReader(typeSystem); ILReader reader = new ILReader(typeSystem);
ILFunction il = reader.ReadIL(method.Body, options.CancellationToken); ILFunction il = reader.ReadIL(method.Body, options.CancellationToken);
foreach (var transform in transforms) { il.RunTransforms(transforms, new ILTransformContext { TypeSystem = typeSystem });
transform.Run(il, new ILTransformContext { TypeSystem = typeSystem });
}
il.WriteTo(output); il.WriteTo(output);
} }
} }

Loading…
Cancel
Save