diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
index 546cd2ea7..d37764e45 100644
--- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
+++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
@@ -61,6 +61,7 @@
+
@@ -91,6 +92,7 @@
+
diff --git a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
index 49c3bca2c..d9a9e2ede 100644
--- a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
+++ b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs
@@ -196,6 +196,12 @@ namespace ICSharpCode.Decompiler.Tests
Run();
}
+ [Test]
+ public void CallIndirect()
+ {
+ Run();
+ }
+
[Test]
public void FSharpLoops_Debug()
{
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CallIndirect.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CallIndirect.cs
new file mode 100644
index 000000000..a87ff898e
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CallIndirect.cs
@@ -0,0 +1,8 @@
+using System;
+internal class CallIndirect
+{
+ private unsafe void Test(IntPtr f)
+ {
+ ((delegate* stdcall)f)(42);
+ }
+}
\ No newline at end of file
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CallIndirect.il b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CallIndirect.il
new file mode 100644
index 000000000..8ba3b409a
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CallIndirect.il
@@ -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
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.cs
index f32b017d4..4bbc8dd9a 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.cs
@@ -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;
[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(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
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void* AsPointer(ref T value)
{
- return &value;
+ return Unsafe.AsPointer(ref value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -151,9 +166,9 @@ namespace System.Runtime.CompilerServices
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public unsafe static ref TTo As(ref TFrom source)
+ public static ref TTo As(ref TFrom source)
{
- return ref *(TTo*)(&source);
+ return ref Unsafe.As(ref source);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.il b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.il
index 7ea5061df..e2b140d3f 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.il
+++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.il
@@ -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
+ }
+}
\ No newline at end of file
diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
index 9ae593ab0..cc77d11e1 100644
--- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
+++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
@@ -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 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
return arg;
case ConversionKind.StopGCTracking:
if (inputType.Kind == TypeKind.ByReference) {
- // cast to corresponding pointer type:
- var pointerType = new PointerType(((ByReferenceType)inputType).ElementType);
- return arg.ConvertTo(pointerType, this).WithILInstruction(inst);
+ 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
.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";
diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
index 1d3f99617..01c7a3d69 100644
--- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
+++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
@@ -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);
diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
index 52ffa500e..c84b2850b 100644
--- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
+++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
@@ -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);
diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs b/ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs
index 3b3a0b216..73a4862db 100644
--- a/ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs
+++ b/ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs
@@ -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
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
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);
diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/FunctionPointerType.cs b/ICSharpCode.Decompiler/CSharp/Syntax/FunctionPointerType.cs
new file mode 100644
index 000000000..4352f7ffa
--- /dev/null
+++ b/ICSharpCode.Decompiler/CSharp/Syntax/FunctionPointerType.cs
@@ -0,0 +1,83 @@
+//
+// FullTypeName.cs
+//
+// Author:
+// Mike Krüger
+//
+// 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 CallingConventionRole = new Role("Target", Identifier.Null);
+
+ public string CallingConvention {
+ get {
+ return GetChildByRole (CallingConventionRole).Name;
+ }
+ set {
+ SetChildByRole (CallingConventionRole, Identifier.Create (value));
+ }
+ }
+
+ public Identifier CallingConventionIdentifier => GetChildByRole(CallingConventionRole);
+
+ public AstNodeCollection TypeArguments {
+ get { return GetChildrenByRole (Roles.TypeArgument); }
+ }
+
+ public override void AcceptVisitor (IAstVisitor visitor)
+ {
+ visitor.VisitFunctionPointerType(this);
+ }
+
+ public override T AcceptVisitor (IAstVisitor visitor)
+ {
+ return visitor.VisitFunctionPointerType(this);
+ }
+
+ public override S AcceptVisitor (IAstVisitor 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();
+ }
+ }
+}
+
diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs b/ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs
index 9ed151865..f9c2976ad 100644
--- a/ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs
+++ b/ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs
@@ -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
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
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);
diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs
index 03e08bba6..cd97b257b 100644
--- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs
+++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceUnsafeModifier.cs
@@ -73,7 +73,12 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
else
return base.VisitComposedType(composedType);
}
-
+
+ public override bool VisitFunctionPointerType(FunctionPointerType functionPointerType)
+ {
+ return true;
+ }
+
public override bool VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression)
{
bool result = base.VisitUnaryOperatorExpression(unaryOperatorExpression);
diff --git a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs
index bbdceed64..4b04441a3 100644
--- a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs
+++ b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs
@@ -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
.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(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);
@@ -472,7 +483,17 @@ namespace ICSharpCode.Decompiler.CSharp
}
return castExpr.WithoutILInstruction().WithRR(rr);
}
-
+
+ bool IsFixedVariable()
+ {
+ if (this.Expression is DirectionExpression dirExpr) {
+ var inst = dirExpr.Expression.Annotation();
+ return inst != null && PointerArithmeticOffset.IsFixedVariable(inst);
+ } else {
+ return false;
+ }
+ }
+
///
/// Gets whether an implicit conversion from 'inputType' to 'newTargetType'
/// would have the same semantics as the existing cast from 'inputType' to 'oldTargetType'.
diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
index b207c5f6d..e3af17f9b 100644
--- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
+++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
@@ -70,6 +70,7 @@
+
diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs b/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs
index ec9d7a088..14c4ea687 100644
--- a/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs
+++ b/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs
@@ -453,24 +453,32 @@ 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)
- {
+ if (br != null && br.TargetBlock.IncomingEdgeCount == 1
+ && 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;
@@ -478,12 +486,35 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
br.TargetBlock.Instructions.RemoveAt(0);
}
}
-
+
body.Blocks.Add(innerBlock); // move block into body
- sourceContainer.Blocks[i] = new Block(); // replace with dummy block
- // we'll delete the dummy block later
+ 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()) {
+ 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);
@@ -618,7 +649,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
ReplacePinnedVar(oldVar, newVar, child);
}
}
-
+
private bool IsSlotAcceptingBothManagedAndUnmanagedPointers(SlotInfo slotInfo)
{
return slotInfo == Block.InstructionSlot || slotInfo == LdObj.TargetSlot || slotInfo == StObj.TargetSlot;
@@ -634,7 +665,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
&& left.MatchLdLoc(nativeVar) && IsNullOrZero(right)
&& trueInst.MatchBranch(out targetBlock);
}
-
+
void HandleStringToPointer(PinnedRegion pinnedRegion)
{
Debug.Assert(pinnedRegion.Variable.Type.IsKnownType(KnownTypeCode.String));
diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs
index c5638eeb2..e86597bd9 100644
--- a/ICSharpCode.Decompiler/IL/ILReader.cs
+++ b/ICSharpCode.Decompiler/IL/ILReader.cs
@@ -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);
diff --git a/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs b/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs
index 20ac323a7..fb83552da 100644
--- a/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs
@@ -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 Arguments;
ILInstruction functionPointer;
+ public readonly InstructionCollection Arguments;
public bool IsInstance { get; }
public bool HasExplicitThis { get; }
public System.Reflection.Metadata.SignatureCallingConvention CallingConvention { get; }
@@ -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 parameterTypes,
- IEnumerable arguments, ILInstruction functionPointer) : base(OpCode.CallIndirect)
+ ILInstruction functionPointer, IEnumerable 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(this, 0);
- this.Arguments.AddRange(arguments);
this.FunctionPointer = functionPointer;
+ this.Arguments = new InstructionCollection(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
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
- output.Write(", ");
+ 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
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
protected override InstructionFlags ComputeFlags()
{
var flags = this.DirectFlags;
+ flags |= functionPointer.Flags;
foreach (var inst in Arguments) {
flags |= inst.Flags;
}
- flags |= functionPointer.Flags;
return flags;
}
diff --git a/ICSharpCode.Decompiler/IL/PointerArithmeticOffset.cs b/ICSharpCode.Decompiler/IL/PointerArithmeticOffset.cs
index 4a32b9358..f1e7032dc 100644
--- a/ICSharpCode.Decompiler/IL/PointerArithmeticOffset.cs
+++ b/ICSharpCode.Decompiler/IL/PointerArithmeticOffset.cs
@@ -82,5 +82,22 @@ namespace ICSharpCode.Decompiler.IL
}
return null;
}
+
+
+ ///
+ /// Returns true if inst 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)
+ ///
+ 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;
+ }
+ }
}
}