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 @@ -49,7 +49,8 @@ namespace ICSharpCode.Decompiler.CSharp
List<IILTransform> ilTransforms = new List<IILTransform> {
new SplitVariables(),
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 IntroduceExitPoints(),
new ConditionDetection(),
@ -287,6 +288,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -287,6 +288,7 @@ namespace ICSharpCode.Decompiler.CSharp
{
if (definitions == null)
throw new ArgumentNullException("definitions");
ITypeDefinition parentTypeDef = null;
var syntaxTree = new SyntaxTree();
foreach (var def in definitions) {
if (def == null)
@ -301,31 +303,36 @@ namespace ICSharpCode.Decompiler.CSharp @@ -301,31 +303,36 @@ namespace ICSharpCode.Decompiler.CSharp
if (typeDef == null)
throw new InvalidOperationException("Could not find type definition in NR type system");
syntaxTree.Members.Add(DoDecompile(typeDef, new SimpleTypeResolveContext(typeDef)));
parentTypeDef = typeDef.DeclaringTypeDefinition;
} else if (methodDefinition != null) {
IMethod method = typeSystem.Resolve(methodDefinition);
if (method == null)
throw new InvalidOperationException("Could not find method definition in NR type system");
syntaxTree.Members.Add(DoDecompile(methodDefinition, method, new SimpleTypeResolveContext(method)));
parentTypeDef = method.DeclaringTypeDefinition;
} else if (fieldDefinition != null) {
IField field = typeSystem.Resolve(fieldDefinition);
if (field == null)
throw new InvalidOperationException("Could not find field definition in NR type system");
syntaxTree.Members.Add(DoDecompile(fieldDefinition, field, new SimpleTypeResolveContext(field)));
parentTypeDef = field.DeclaringTypeDefinition;
} else if (propertyDefinition != null) {
IProperty property = typeSystem.Resolve(propertyDefinition);
if (property == null)
throw new InvalidOperationException("Could not find field definition in NR type system");
syntaxTree.Members.Add(DoDecompile(propertyDefinition, property, new SimpleTypeResolveContext(property)));
parentTypeDef = property.DeclaringTypeDefinition;
} else if (eventDefinition != null) {
IEvent ev = typeSystem.Resolve(eventDefinition);
if (ev == null)
throw new InvalidOperationException("Could not find field definition in NR type system");
syntaxTree.Members.Add(DoDecompile(eventDefinition, ev, new SimpleTypeResolveContext(ev)));
parentTypeDef = ev.DeclaringTypeDefinition;
} else {
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;
}

6
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -490,8 +490,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -490,8 +490,10 @@ namespace ICSharpCode.Decompiler.CSharp
var left = Translate(inst.Left);
var right = Translate(inst.Right);
ResolveResult rr;
if (left.Type.IsKnownType(KnownTypeCode.IntPtr) || left.Type.IsKnownType(KnownTypeCode.UIntPtr)
|| right.Type.IsKnownType(KnownTypeCode.IntPtr) || right.Type.IsKnownType(KnownTypeCode.UIntPtr)) {
if (left.Type.GetStackType() == StackType.I || right.Type.GetStackType() == StackType.I) {
// 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;
if (inst.Sign == Sign.Unsigned) {
targetType = compilation.FindType(KnownTypeCode.UInt64);

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

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

2
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

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

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

@ -0,0 +1,164 @@ @@ -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 @@ -1071,19 +1071,27 @@ namespace ICSharpCode.Decompiler.IL
switch (condition.ResultType) {
case StackType.O:
// 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;
case StackType.I:
// 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;
case StackType.I8:
// 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;
}
if (negate) {
condition = new LogicNot(condition);
}
MarkBranchTarget(target);
return new IfInstruction(condition, new Branch(target));

124
ICSharpCode.Decompiler/IL/Instructions.cs

@ -39,6 +39,8 @@ namespace ICSharpCode.Decompiler.IL @@ -39,6 +39,8 @@ namespace ICSharpCode.Decompiler.IL
BlockContainer,
/// <summary>A block of IL instructions.</summary>
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>
LogicNot,
/// <summary>Adds two numbers.</summary>
@ -574,6 +576,107 @@ namespace ICSharpCode.Decompiler.IL @@ -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>
public sealed partial class LogicNot : UnaryInstruction
{
@ -3116,6 +3219,10 @@ namespace ICSharpCode.Decompiler.IL @@ -3116,6 +3219,10 @@ namespace ICSharpCode.Decompiler.IL
{
Default(block);
}
protected internal virtual void VisitPinnedRegion(PinnedRegion inst)
{
Default(inst);
}
protected internal virtual void VisitLogicNot(LogicNot inst)
{
Default(inst);
@ -3418,6 +3525,10 @@ namespace ICSharpCode.Decompiler.IL @@ -3418,6 +3525,10 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(block);
}
protected internal virtual T VisitPinnedRegion(PinnedRegion inst)
{
return Default(inst);
}
protected internal virtual T VisitLogicNot(LogicNot inst)
{
return Default(inst);
@ -3742,6 +3853,7 @@ namespace ICSharpCode.Decompiler.IL @@ -3742,6 +3853,7 @@ namespace ICSharpCode.Decompiler.IL
"ILFunction",
"BlockContainer",
"Block",
"PinnedRegion",
"logic.not",
"add",
"sub",
@ -3831,6 +3943,18 @@ namespace ICSharpCode.Decompiler.IL @@ -3831,6 +3943,18 @@ namespace ICSharpCode.Decompiler.IL
}
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)
{
var inst = this as LogicNot;

6
ICSharpCode.Decompiler/IL/Instructions.tt

@ -49,6 +49,12 @@ @@ -49,6 +49,12 @@
VoidResult, CustomConstructor, CustomVariableName("container")),
new OpCode("Block", "A block of IL instructions.",
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).",
ResultType("I4"), Unary),
new OpCode("add", "Adds two numbers.", BinaryNumeric),

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

@ -36,10 +36,11 @@ namespace ICSharpCode.Decompiler.IL @@ -36,10 +36,11 @@ namespace ICSharpCode.Decompiler.IL
/// the FinalInstruction (which is not part of the list) will be executed.
/// The block returns returns the result value of the FinalInstruction.
/// 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>
///
/// 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>
partial class Block : ILInstruction
{
@ -59,6 +60,13 @@ namespace ICSharpCode.Decompiler.IL @@ -59,6 +60,13 @@ namespace ICSharpCode.Decompiler.IL
/// </remarks>
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 {
get {
return finalInstruction;

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

@ -69,5 +69,17 @@ namespace ICSharpCode.Decompiler.IL @@ -69,5 +69,17 @@ namespace ICSharpCode.Decompiler.IL
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 @@ -267,6 +267,16 @@ namespace ICSharpCode.Decompiler.IL
}
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>
/// Removes all elements for which the predicate returns true.
@ -310,6 +320,11 @@ namespace ICSharpCode.Decompiler.IL @@ -310,6 +320,11 @@ namespace ICSharpCode.Decompiler.IL
return list[list.Count - 1];
}
public T LastOrDefault()
{
return list.Count > 0 ? list[list.Count - 1] : null;
}
public T ElementAtOrDefault(int index)
{
if (index >= 0 && index < list.Count)

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

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

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

@ -52,7 +52,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -52,7 +52,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
default:
// parameters: avoid splitting parameters
// 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;
}
}

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

@ -23,7 +23,7 @@ public class UnsafeCode @@ -23,7 +23,7 @@ public class UnsafeCode
static void Main()
{
// 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
@ -72,7 +72,25 @@ public class UnsafeCode @@ -72,7 +72,25 @@ public class UnsafeCode
fixed (double* ptr = &matrix[1, 2])
{
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)

2
ICSharpCode.Decompiler/Tests/TestRunner.cs

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

170
ICSharpCode.Decompiler/Tests/UnsafeCode.cs

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

4
ILSpy/Languages/ILAstLanguage.cs

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

Loading…
Cancel
Save