Browse Source

Merge pull request #2045 from icsharpcode/unsafe

Improved decompilation of unsafe code
pull/2055/head
Daniel Grunwald 5 years ago committed by GitHub
parent
commit
1f8a67a7db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 6
      ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
  3. 8
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CallIndirect.cs
  4. 39
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CallIndirect.il
  5. 21
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.cs
  6. 38
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.il
  7. 37
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  8. 13
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  9. 14
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  10. 15
      ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs
  11. 83
      ICSharpCode.Decompiler/CSharp/Syntax/FunctionPointerType.cs
  12. 3
      ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs
  13. 5
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs
  14. 21
      ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs
  15. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  16. 37
      ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs
  17. 4
      ICSharpCode.Decompiler/IL/ILReader.cs
  18. 46
      ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs
  19. 17
      ICSharpCode.Decompiler/IL/PointerArithmeticOffset.cs

2
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -61,6 +61,7 @@ @@ -61,6 +61,7 @@
<None Include="TestCases\Correctness\StackTests.il" />
<None Include="TestCases\Correctness\StackTypes.il" />
<None Include="TestCases\Correctness\Uninit.vb" />
<None Include="TestCases\ILPretty\CallIndirect.il" />
<None Include="TestCases\ILPretty\Issue1454.il" />
<None Include="TestCases\ILPretty\Issue1681.il" />
<None Include="TestCases\ILPretty\Issue1922.il" />
@ -91,6 +92,7 @@ @@ -91,6 +92,7 @@
<Compile Include="ProjectDecompiler\TargetFrameworkTests.cs" />
<Compile Include="TestAssemblyResolver.cs" />
<Compile Include="TestCases\Correctness\StringConcat.cs" />
<None Include="TestCases\ILPretty\CallIndirect.cs" />
<Compile Include="TestCases\ILPretty\Issue1681.cs" />
<Compile Include="TestCases\Ugly\AggressiveScalarReplacementOfAggregates.cs" />
<None Include="TestCases\Ugly\AggressiveScalarReplacementOfAggregates.Expected.cs" />

6
ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs

@ -196,6 +196,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -196,6 +196,12 @@ namespace ICSharpCode.Decompiler.Tests
Run();
}
[Test]
public void CallIndirect()
{
Run();
}
[Test]
public void FSharpLoops_Debug()
{

8
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CallIndirect.cs

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
using System;
internal class CallIndirect
{
private unsafe void Test(IntPtr f)
{
((delegate* stdcall<int, void>)f)(42);
}
}

39
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CallIndirect.il

@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
#define CORE_ASSEMBLY "System.Runtime"
.assembly extern CORE_ASSEMBLY
{
.publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....:
.ver 4:0:0:0
}
.class private auto ansi beforefieldinit CallIndirect
extends [System.Private.CoreLib]System.Object
{
// Methods
.method private hidebysig
instance void Test (
native int f
) cil managed
{
.maxstack 8
ldc.i4.s 42
ldarg.1
calli unmanaged stdcall void(int32)
ret
} // end of method Example::Test
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x206e
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [System.Private.CoreLib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method Example::.ctor
} // end of class Example

21
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.cs

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
[assembly: AssemblyFileVersion("4.0.0.0")]
[assembly: AssemblyInformationalVersion("4.0.0.0")]
@ -12,6 +13,20 @@ using System.Reflection; @@ -12,6 +13,20 @@ using System.Reflection;
[assembly: AssemblyProduct("Microsoft® .NET Framework")]
[assembly: CLSCompliant(false)]
internal sealed class ExtraUnsafeTests
{
public unsafe static void PinWithTypeMismatch(ref uint managedPtr)
{
fixed (ushort* ptr = &Unsafe.As<uint, ushort>(ref managedPtr)) {
}
}
public unsafe static uint* RefToPointerWithoutPinning(ref uint managedPtr)
{
return (uint*)Unsafe.AsPointer(ref managedPtr);
}
}
namespace System.Runtime.CompilerServices
{
public static class Unsafe
@ -67,7 +82,7 @@ namespace System.Runtime.CompilerServices @@ -67,7 +82,7 @@ namespace System.Runtime.CompilerServices
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void* AsPointer<T>(ref T value)
{
return &value;
return Unsafe.AsPointer(ref value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -151,9 +166,9 @@ namespace System.Runtime.CompilerServices @@ -151,9 +166,9 @@ namespace System.Runtime.CompilerServices
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static ref TTo As<TFrom, TTo>(ref TFrom source)
public static ref TTo As<TFrom, TTo>(ref TFrom source)
{
return ref *(TTo*)(&source);
return ref Unsafe.As<TFrom, TTo>(ref source);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

38
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.il

@ -437,3 +437,41 @@ @@ -437,3 +437,41 @@
} // end of class System.Runtime.CompilerServices.IsReadOnlyAttribute
#endif
.class private auto ansi sealed beforefieldinit ExtraUnsafeTests
extends [CORE_ASSEMBLY]System.Object
{
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
.maxstack 1
ldarg.0
call instance void [CORE_ASSEMBLY]System.Object::.ctor()
ret
} // end of method ExtraUnsafeTests::.ctor
.method public hidebysig static void PinWithTypeMismatch(uint32& managedPtr)
{
.maxstack 8
.locals (
[0] uint16& pinned
)
// Pin:
ldarg.0
stloc.0
// Unpin:
ldc.i4 0
stloc.0
ret
}
.method public hidebysig static uint32* RefToPointerWithoutPinning(uint32& managedPtr)
{
.maxstack 8
ldarg.0
ret
}
}

37
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1138,13 +1138,17 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1138,13 +1138,17 @@ namespace ICSharpCode.Decompiler.CSharp
return null;
}
internal TranslatedExpression CallUnsafeIntrinsic(string name, Expression[] arguments, IType returnType, ILInstruction inst)
internal TranslatedExpression CallUnsafeIntrinsic(string name, Expression[] arguments, IType returnType, ILInstruction inst = null, IEnumerable<IType> typeArguments = null)
{
var target = new MemberReferenceExpression {
Target = new TypeReferenceExpression(astBuilder.ConvertType(compilation.FindType(KnownTypeCode.Unsafe))),
MemberName = name
};
var invocation = new InvocationExpression(target, arguments).WithILInstruction(inst);
if (typeArguments != null) {
target.TypeArguments.AddRange(typeArguments.Select(astBuilder.ConvertType));
}
var invocationExpr = new InvocationExpression(target, arguments);
var invocation = inst != null ? invocationExpr.WithILInstruction(inst) : invocationExpr.WithoutILInstruction();
if (returnType is ByReferenceType brt) {
return WrapInRef(invocation.WithRR(new ResolveResult(brt.ElementType)), brt);
} else {
@ -1672,9 +1676,17 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1672,9 +1676,17 @@ namespace ICSharpCode.Decompiler.CSharp
return arg;
case ConversionKind.StopGCTracking:
if (inputType.Kind == TypeKind.ByReference) {
if (PointerArithmeticOffset.IsFixedVariable(inst.Argument)) {
// cast to corresponding pointer type:
var pointerType = new PointerType(((ByReferenceType)inputType).ElementType);
return arg.ConvertTo(pointerType, this).WithILInstruction(inst);
} else {
// emit Unsafe.AsPointer() intrinsic:
return CallUnsafeIntrinsic("AsPointer",
arguments: new Expression[] { arg },
returnType: new PointerType(compilation.FindType(KnownTypeCode.Void)),
inst: inst);
}
} else if (arg.Type.GetStackType().IsIntegerType()) {
// ConversionKind.StopGCTracking should only be used with managed references,
// but it's possible that we're supposed to stop tracking something we just started to track.
@ -3244,6 +3256,27 @@ namespace ICSharpCode.Decompiler.CSharp @@ -3244,6 +3256,27 @@ namespace ICSharpCode.Decompiler.CSharp
.WithILInstruction(inst);
}
protected internal override TranslatedExpression VisitCallIndirect(CallIndirect inst, TranslationContext context)
{
if (inst.IsInstance) {
return ErrorExpression("calli with instance method signature not supportd");
}
var ty = new FunctionPointerType();
if (inst.CallingConvention != System.Reflection.Metadata.SignatureCallingConvention.Default) {
ty.CallingConvention = inst.CallingConvention.ToString().ToLowerInvariant();
}
foreach (var parameterType in inst.ParameterTypes) {
ty.TypeArguments.Add(astBuilder.ConvertType(parameterType));
}
ty.TypeArguments.Add(astBuilder.ConvertType(inst.ReturnType));
var functionPointer = Translate(inst.FunctionPointer);
var invocation = new InvocationExpression(new CastExpression(ty, functionPointer));
foreach (var (arg, paramType) in inst.Arguments.Zip(inst.ParameterTypes)) {
invocation.Arguments.Add(Translate(arg, typeHint: paramType).ConvertTo(paramType, this, allowImplicitConversion: true));
}
return invocation.WithRR(new ResolveResult(inst.ReturnType)).WithILInstruction(inst);
}
protected internal override TranslatedExpression VisitInvalidBranch(InvalidBranch inst, TranslationContext context)
{
string message = "Error";

13
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs

@ -2335,6 +2335,19 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -2335,6 +2335,19 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
EndNode(tupleTypeElement);
}
public virtual void VisitFunctionPointerType(FunctionPointerType functionPointerType)
{
StartNode(functionPointerType);
WriteKeyword(Roles.DelegateKeyword);
WriteToken(FunctionPointerType.PointerRole);
if (!functionPointerType.CallingConventionIdentifier.IsNull) {
Space();
WriteIdentifier(functionPointerType.CallingConventionIdentifier);
}
WriteTypeArguments(functionPointerType.TypeArguments);
EndNode(functionPointerType);
}
public virtual void VisitComposedType(ComposedType composedType)
{
StartNode(composedType);

14
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -832,7 +832,19 @@ namespace ICSharpCode.Decompiler.CSharp @@ -832,7 +832,19 @@ namespace ICSharpCode.Decompiler.CSharp
initExpr = exprBuilder.Translate(gpr.Argument);
}
} else {
initExpr = exprBuilder.Translate(inst.Init, typeHint: inst.Variable.Type).ConvertTo(inst.Variable.Type, exprBuilder);
IType refType = inst.Variable.Type;
if (refType is PointerType pointerType) {
refType = new ByReferenceType(pointerType.ElementType);
}
initExpr = exprBuilder.Translate(inst.Init, typeHint: refType).ConvertTo(refType, exprBuilder);
if (initExpr is DirectionExpression dirExpr) {
if (dirExpr.Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.Dereference) {
initExpr = uoe.Expression.Detach();
} else {
initExpr = new UnaryOperatorExpression(UnaryOperatorType.AddressOf, dirExpr.Expression.Detach())
.WithRR(new ResolveResult(inst.Variable.Type));
}
}
}
fixedStmt.Variables.Add(new VariableInitializer(inst.Variable.Name, initExpr).WithILVariable(inst.Variable));
fixedStmt.EmbeddedStatement = Convert(inst.Body);

15
ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs

@ -126,6 +126,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -126,6 +126,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
VisitChildren (tupleTypeElement);
}
public virtual void VisitFunctionPointerType(FunctionPointerType functionPointerType)
{
VisitChildren(functionPointerType);
}
public virtual void VisitAttribute (Attribute attribute)
{
VisitChildren (attribute);
@ -778,6 +783,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -778,6 +783,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return VisitChildren (tupleTypeElement);
}
public virtual T VisitFunctionPointerType(FunctionPointerType functionPointerType)
{
return VisitChildren (functionPointerType);
}
public virtual T VisitAttribute (Attribute attribute)
{
return VisitChildren (attribute);
@ -1430,6 +1440,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1430,6 +1440,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return VisitChildren (tupleTypeElement, data);
}
public virtual S VisitFunctionPointerType(FunctionPointerType functionPointerType, T data)
{
return VisitChildren (functionPointerType, data);
}
public virtual S VisitAttribute (Attribute attribute, T data)
{
return VisitChildren (attribute, data);

83
ICSharpCode.Decompiler/CSharp/Syntax/FunctionPointerType.cs

@ -0,0 +1,83 @@ @@ -0,0 +1,83 @@
//
// FullTypeName.cs
//
// Author:
// Mike Krüger <mkrueger@novell.com>
//
// Copyright (c) 2010 Novell, Inc (http://www.novell.com)
//
// 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 ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.CSharp.TypeSystem;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.CSharp.Syntax
{
public class FunctionPointerType : AstType
{
public static readonly TokenRole PointerRole = new TokenRole("*");
public static readonly Role<Identifier> CallingConventionRole = new Role<Identifier>("Target", Identifier.Null);
public string CallingConvention {
get {
return GetChildByRole (CallingConventionRole).Name;
}
set {
SetChildByRole (CallingConventionRole, Identifier.Create (value));
}
}
public Identifier CallingConventionIdentifier => GetChildByRole(CallingConventionRole);
public AstNodeCollection<AstType> TypeArguments {
get { return GetChildrenByRole (Roles.TypeArgument); }
}
public override void AcceptVisitor (IAstVisitor visitor)
{
visitor.VisitFunctionPointerType(this);
}
public override T AcceptVisitor<T> (IAstVisitor<T> visitor)
{
return visitor.VisitFunctionPointerType(this);
}
public override S AcceptVisitor<T, S> (IAstVisitor<T, S> visitor, T data)
{
return visitor.VisitFunctionPointerType(this, data);
}
protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
{
return other is FunctionPointerType o && MatchString(this.CallingConvention, o.CallingConvention)
&& this.TypeArguments.DoMatch(o.TypeArguments, match);
}
public override ITypeReference ToTypeReference(NameLookupMode lookupMode, InterningProvider interningProvider = null)
{
throw new NotImplementedException();
}
}
}

3
ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs

@ -137,6 +137,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -137,6 +137,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
void VisitMemberType(MemberType memberType);
void VisitTupleType(TupleAstType tupleType);
void VisitTupleTypeElement(TupleTypeElement tupleTypeElement);
void VisitFunctionPointerType(FunctionPointerType functionPointerType);
void VisitComposedType(ComposedType composedType);
void VisitArraySpecifier(ArraySpecifier arraySpecifier);
void VisitPrimitiveType(PrimitiveType primitiveType);
@ -279,6 +280,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -279,6 +280,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
S VisitMemberType(MemberType memberType);
S VisitTupleType(TupleAstType tupleType);
S VisitTupleTypeElement(TupleTypeElement tupleTypeElement);
S VisitFunctionPointerType(FunctionPointerType functionPointerType);
S VisitComposedType(ComposedType composedType);
S VisitArraySpecifier(ArraySpecifier arraySpecifier);
S VisitPrimitiveType(PrimitiveType primitiveType);
@ -421,6 +423,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -421,6 +423,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
S VisitMemberType(MemberType memberType, T data);
S VisitTupleType(TupleAstType tupleType, T data);
S VisitTupleTypeElement(TupleTypeElement tupleTypeElement, T data);
S VisitFunctionPointerType(FunctionPointerType functionPointerType, T data);
S VisitComposedType(ComposedType composedType, T data);
S VisitArraySpecifier(ArraySpecifier arraySpecifier, T data);
S VisitPrimitiveType(PrimitiveType primitiveType, T data);

5
ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs

@ -74,6 +74,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -74,6 +74,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
return base.VisitComposedType(composedType);
}
public override bool VisitFunctionPointerType(FunctionPointerType functionPointerType)
{
return true;
}
public override bool VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression)
{
bool result = base.VisitUnaryOperatorExpression(unaryOperatorExpression);

21
ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs

@ -388,6 +388,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -388,6 +388,9 @@ namespace ICSharpCode.Decompiler.CSharp
return pointerExpr.ConvertTo(targetType, expressionBuilder);
}
if (targetType.Kind == TypeKind.ByReference) {
if (NormalizeTypeVisitor.TypeErasure.EquivalentTypes(targetType, this.Type)) {
return this;
}
var elementType = ((ByReferenceType)targetType).ElementType;
if (this.Expression is DirectionExpression thisDir && this.ILInstructions.Any(i => i.OpCode == OpCode.AddressOf)
&& thisDir.Expression.GetResolveResult()?.Type.GetStackType() == elementType.GetStackType()) {
@ -398,6 +401,14 @@ namespace ICSharpCode.Decompiler.CSharp @@ -398,6 +401,14 @@ namespace ICSharpCode.Decompiler.CSharp
.WithILInstruction(this.ILInstructions)
.WithRR(new ByReferenceResolveResult(convertedTemp.ResolveResult, ReferenceKind.Ref));
}
if (this.Type.Kind == TypeKind.ByReference && !IsFixedVariable()) {
// Convert between managed reference types.
// We can't do this by going through a pointer type because that would temporarily stop GC tracking.
// Instead, emit `ref Unsafe.As<T>(ref expr)`
return expressionBuilder.CallUnsafeIntrinsic("As", new[] { this.Expression },
typeArguments: new IType[] { ((ByReferenceType)this.Type).ElementType, elementType },
returnType: targetType);
}
// Convert from integer/pointer to reference.
// First, convert to the corresponding pointer type:
var arg = this.ConvertTo(new PointerType(elementType), expressionBuilder, checkForOverflow);
@ -473,6 +484,16 @@ namespace ICSharpCode.Decompiler.CSharp @@ -473,6 +484,16 @@ namespace ICSharpCode.Decompiler.CSharp
return castExpr.WithoutILInstruction().WithRR(rr);
}
bool IsFixedVariable()
{
if (this.Expression is DirectionExpression dirExpr) {
var inst = dirExpr.Expression.Annotation<ILInstruction>();
return inst != null && PointerArithmeticOffset.IsFixedVariable(inst);
} else {
return false;
}
}
/// <summary>
/// Gets whether an implicit conversion from 'inputType' to 'newTargetType'
/// would have the same semantics as the existing cast from 'inputType' to 'oldTargetType'.

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -70,6 +70,7 @@ @@ -70,6 +70,7 @@
<Compile Include="CSharp\SequencePointBuilder.cs" />
<Compile Include="CSharp\ProjectDecompiler\TargetFramework.cs" />
<Compile Include="CSharp\ProjectDecompiler\TargetServices.cs" />
<Compile Include="CSharp\Syntax\FunctionPointerType.cs" />
<Compile Include="IL\Transforms\IndexRangeTransform.cs" />
<Compile Include="CSharp\TranslatedStatement.cs" />
<Compile Include="DebugInfo\KnownGuids.cs" />

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

@ -456,21 +456,29 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -456,21 +456,29 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// 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)
bool cloneBlocks = false;
for (int i = 0; i < sourceContainer.Blocks.Count; i++) {
if (reachedEdgesPerBlock[i] != 0 && reachedEdgesPerBlock[i] != sourceContainer.Blocks[i].IncomingEdgeCount) {
return false;
// Don't abort in this case, we still need to somehow represent the pinned variable with a fixed statement.
// We'll duplicate the code so that it can be both inside and outside the pinned region.
cloneBlocks = true;
break;
}
}
context.Step("CreatePinnedRegion", block);
BlockContainer body = new BlockContainer();
Block[] clonedBlocks = cloneBlocks ? new Block[sourceContainer.Blocks.Count] : null;
for (int i = 0; i < sourceContainer.Blocks.Count; i++) {
if (reachedEdgesPerBlock[i] > 0) {
var innerBlock = sourceContainer.Blocks[i];
if (cloneBlocks) {
innerBlock = (Block)innerBlock.Clone();
clonedBlocks[i] = innerBlock;
}
Branch br = innerBlock.Instructions.LastOrDefault() as Branch;
if (br != null && br.TargetBlock.IncomingEdgeCount == 1
&& br.TargetContainer == sourceContainer && reachedEdgesPerBlock[br.TargetBlock.ChildIndex] == 0)
{
&& br.TargetContainer == sourceContainer && reachedEdgesPerBlock[br.TargetBlock.ChildIndex] == 0) {
// branch that leaves body.
// The target block should have an instruction that resets the pin; delete that instruction:
StLoc unpin = br.TargetBlock.Instructions.First() as StLoc;
@ -480,10 +488,33 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -480,10 +488,33 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
body.Blocks.Add(innerBlock); // move block into body
if (!cloneBlocks) {
sourceContainer.Blocks[i] = new Block(); // replace with dummy block
// we'll delete the dummy block later
}
}
}
if (cloneBlocks) {
// Adjust branches between cloned blocks.
foreach (var branch in body.Descendants.OfType<Branch>()) {
if (branch.TargetContainer == sourceContainer) {
int i = branch.TargetBlock.ChildIndex;
if (clonedBlocks[i] != null) {
branch.TargetBlock = clonedBlocks[i];
}
}
}
// Replace unreachable blocks in sourceContainer with dummy blocks:
bool[] isAlive = new bool[sourceContainer.Blocks.Count];
foreach (var remainingBlock in sourceContainer.TopologicalSort(deleteUnreachableBlocks: true)) {
isAlive[remainingBlock.ChildIndex] = true;
}
for (int i = 0; i < isAlive.Length; i++) {
if (!isAlive[i])
sourceContainer.Blocks[i] = new Block();
}
// we'll delete the dummy blocks later
}
if (body.Blocks.Count == 0) {
// empty body, the entryBlock itself doesn't belong into the pinned region
Debug.Assert(reachedEdgesPerBlock[entryBlock.ChildIndex] == 0);

4
ICSharpCode.Decompiler/IL/ILReader.cs

@ -1456,8 +1456,8 @@ namespace ICSharpCode.Decompiler.IL @@ -1456,8 +1456,8 @@ namespace ICSharpCode.Decompiler.IL
signature.Header.CallingConvention,
signature.ReturnType,
signature.ParameterTypes,
arguments,
functionPointer
functionPointer,
arguments
);
if (call.ResultType != StackType.Void)
return Push(call);

46
ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs

@ -27,11 +27,13 @@ namespace ICSharpCode.Decompiler.IL @@ -27,11 +27,13 @@ namespace ICSharpCode.Decompiler.IL
{
partial class CallIndirect
{
public static readonly SlotInfo ArgumentSlot = new SlotInfo("Argument", canInlineInto: true, isCollection: true);
// Note: while in IL the arguments come first and the function pointer last;
// in the ILAst we're handling it as in C#: the function pointer is evaluated first, the arguments later.
public static readonly SlotInfo FunctionPointerSlot = new SlotInfo("FunctionPointer", canInlineInto: true);
public static readonly SlotInfo ArgumentSlot = new SlotInfo("Argument", canInlineInto: true, isCollection: true);
public readonly InstructionCollection<ILInstruction> Arguments;
ILInstruction functionPointer;
public readonly InstructionCollection<ILInstruction> Arguments;
public bool IsInstance { get; }
public bool HasExplicitThis { get; }
public System.Reflection.Metadata.SignatureCallingConvention CallingConvention { get; }
@ -51,34 +53,27 @@ namespace ICSharpCode.Decompiler.IL @@ -51,34 +53,27 @@ namespace ICSharpCode.Decompiler.IL
}
set {
ValidateChild(value);
SetChildInstruction(ref functionPointer, value, Arguments.Count);
SetChildInstruction(ref functionPointer, value, 0);
}
}
protected internal override void InstructionCollectionUpdateComplete()
{
base.InstructionCollectionUpdateComplete();
if (functionPointer?.Parent == this)
functionPointer.ChildIndex = Arguments.Count;
}
public CallIndirect(bool isInstance, bool hasExplicitThis, System.Reflection.Metadata.SignatureCallingConvention callingConvention, IType returnType, ImmutableArray<IType> parameterTypes,
IEnumerable<ILInstruction> arguments, ILInstruction functionPointer) : base(OpCode.CallIndirect)
ILInstruction functionPointer, IEnumerable<ILInstruction> arguments) : base(OpCode.CallIndirect)
{
this.IsInstance = isInstance;
this.HasExplicitThis = hasExplicitThis;
this.CallingConvention = callingConvention;
this.ReturnType = returnType ?? throw new ArgumentNullException(nameof(returnType));
this.ParameterTypes = parameterTypes.ToImmutableArray();
this.Arguments = new InstructionCollection<ILInstruction>(this, 0);
this.Arguments.AddRange(arguments);
this.FunctionPointer = functionPointer;
this.Arguments = new InstructionCollection<ILInstruction>(this, 1);
this.Arguments.AddRange(arguments);
}
public override ILInstruction Clone()
{
return new CallIndirect(IsInstance, HasExplicitThis, CallingConvention, ReturnType, ParameterTypes,
this.Arguments.Select(inst => inst.Clone()), functionPointer.Clone()
functionPointer.Clone(), this.Arguments.Select(inst => inst.Clone())
).WithILRange(this);
}
@ -96,24 +91,19 @@ namespace ICSharpCode.Decompiler.IL @@ -96,24 +91,19 @@ namespace ICSharpCode.Decompiler.IL
output.Write("call.indirect ");
ReturnType.WriteTo(output);
output.Write('(');
bool first = true;
functionPointer.WriteTo(output, options);
int firstArgument = IsInstance ? 1 : 0;
if (firstArgument == 1) {
output.Write(", ");
Arguments[0].WriteTo(output, options);
first = false;
}
foreach (var (inst, type) in Arguments.Skip(firstArgument).Zip(ParameterTypes, (a,b) => (a,b))) {
if (first)
first = false;
else
foreach (var (inst, type) in Arguments.Zip(ParameterTypes, (a,b) => (a,b))) {
output.Write(", ");
inst.WriteTo(output, options);
output.Write(" : ");
type.WriteTo(output);
}
if (Arguments.Count > 0)
output.Write(", ");
functionPointer.WriteTo(output, options);
output.Write(')');
}
@ -124,22 +114,22 @@ namespace ICSharpCode.Decompiler.IL @@ -124,22 +114,22 @@ namespace ICSharpCode.Decompiler.IL
protected override ILInstruction GetChild(int index)
{
if (index == Arguments.Count)
if (index == 0)
return functionPointer;
return Arguments[index];
return Arguments[index - 1];
}
protected override void SetChild(int index, ILInstruction value)
{
if (index == Arguments.Count)
if (index == 0)
FunctionPointer = value;
else
Arguments[index] = value;
Arguments[index - 1] = value;
}
protected override SlotInfo GetChildSlot(int index)
{
if (index == Arguments.Count)
if (index == 0)
return FunctionPointerSlot;
else
return ArgumentSlot;
@ -148,10 +138,10 @@ namespace ICSharpCode.Decompiler.IL @@ -148,10 +138,10 @@ namespace ICSharpCode.Decompiler.IL
protected override InstructionFlags ComputeFlags()
{
var flags = this.DirectFlags;
flags |= functionPointer.Flags;
foreach (var inst in Arguments) {
flags |= inst.Flags;
}
flags |= functionPointer.Flags;
return flags;
}

17
ICSharpCode.Decompiler/IL/PointerArithmeticOffset.cs

@ -82,5 +82,22 @@ namespace ICSharpCode.Decompiler.IL @@ -82,5 +82,22 @@ namespace ICSharpCode.Decompiler.IL
}
return null;
}
/// <summary>
/// Returns true if <c>inst</c> computes the address of a fixed variable; false if it computes the address of a moveable variable.
/// (see "Fixed and moveable variables" in the C# specification)
/// </summary>
internal static bool IsFixedVariable(ILInstruction inst)
{
switch (inst) {
case LdLoca ldloca:
return ldloca.Variable.CaptureScope == null; // locals are fixed if uncaptured
case LdFlda ldflda:
return IsFixedVariable(ldflda.Target);
default:
return inst.ResultType == StackType.I;
}
}
}
}

Loading…
Cancel
Save