Browse Source

Fix #1333: Ensure we convert to the correct type when calling instance methods on value types

pull/1347/head
Daniel Grunwald 7 years ago
parent
commit
b455286ad3
  1. 39
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/StackTypes.il
  2. 20
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  3. 22
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  4. 11
      ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs

39
ICSharpCode.Decompiler.Tests/TestCases/Correctness/StackTypes.il

@ -22,6 +22,7 @@ extends [mscorlib]System.Object
call void Program::InlineAssignByte() call void Program::InlineAssignByte()
call void Program::Int32OrNativeTests() call void Program::Int32OrNativeTests()
call void Program::ByRefInstanceCallWithTypeMismatchTests()
ret ret
} // end of method Main } // end of method Main
@ -284,4 +285,42 @@ pointless:
call void [mscorlib]System.Console::WriteLine(object) call void [mscorlib]System.Console::WriteLine(object)
ret ret
} }
.method public static void ByRefInstanceCallWithTypeMismatchTests()
{
ldstr "ByRefInstanceCallWithTypeMismatch(0) = {0}"
ldc.i4.0
call string Program::ByRefInstanceCallWithTypeMismatch(int32)
call void [mscorlib]System.Console::WriteLine(string, object)
ldstr "ByRefInstanceCallWithTypeMismatch(1) = {0}"
ldc.i4.1
call string Program::ByRefInstanceCallWithTypeMismatch(int32)
call void [mscorlib]System.Console::WriteLine(string, object)
ldstr "Issue1333() = {0}"
call string Program::Issue1333()
call void [mscorlib]System.Console::WriteLine(string, object)
ret
}
.method public hidebysig static string ByRefInstanceCallWithTypeMismatch(int32 val) cil managed
{
ldarga val
constrained. [mscorlib]System.Boolean
callvirt instance string [mscorlib]System.Object::ToString()
ret
}
.method public hidebysig static string Issue1333() cil managed
{
.locals (bool)
ldc.i4.0
stloc.0
ldloca.s 0
constrained. [mscorlib]System.Boolean
callvirt instance string [mscorlib]System.Object::ToString()
ret
}
} }

20
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -1185,6 +1185,7 @@ namespace ICSharpCode.Decompiler.CSharp
TranslatedExpression HandleDelegateConstruction(CallInstruction inst) TranslatedExpression HandleDelegateConstruction(CallInstruction inst)
{ {
ILInstruction thisArg = inst.Arguments[0];
ILInstruction func = inst.Arguments[1]; ILInstruction func = inst.Arguments[1];
IMethod method; IMethod method;
switch (func.OpCode) { switch (func.OpCode) {
@ -1203,16 +1204,27 @@ namespace ICSharpCode.Decompiler.CSharp
bool requireTarget; bool requireTarget;
if (method.IsExtensionMethod && invokeMethod != null && method.Parameters.Count - 1 == invokeMethod.Parameters.Count) { if (method.IsExtensionMethod && invokeMethod != null && method.Parameters.Count - 1 == invokeMethod.Parameters.Count) {
targetType = method.Parameters[0].Type; targetType = method.Parameters[0].Type;
target = expressionBuilder.Translate(inst.Arguments[0], targetType); if (targetType.Kind == TypeKind.ByReference && thisArg is Box thisArgBox) {
target = ExpressionBuilder.UnwrapBoxingConversion(target); targetType = ((ByReferenceType)targetType).ElementType;
thisArg = thisArgBox.Argument;
}
target = expressionBuilder.Translate(thisArg, targetType);
requireTarget = true; requireTarget = true;
} else { } else {
targetType = method.DeclaringType; targetType = method.DeclaringType;
target = expressionBuilder.TranslateTarget(inst.Arguments[0], if (targetType.IsReferenceType == false && thisArg is Box thisArgBox) {
// Normal struct instance method calls (which TranslateTarget is meant for) expect a 'ref T',
// but delegate construction uses a 'box T'.
if (thisArgBox.Argument is LdObj ldobj) {
thisArg = ldobj.Target;
} else {
thisArg = new AddressOf(thisArgBox.Argument);
}
}
target = expressionBuilder.TranslateTarget(thisArg,
nonVirtualInvocation: func.OpCode == OpCode.LdFtn, nonVirtualInvocation: func.OpCode == OpCode.LdFtn,
memberStatic: method.IsStatic, memberStatic: method.IsStatic,
memberDeclaringType: method.DeclaringType); memberDeclaringType: method.DeclaringType);
target = ExpressionBuilder.UnwrapBoxingConversion(target);
requireTarget = expressionBuilder.HidesVariableWithName(method.Name) requireTarget = expressionBuilder.HidesVariableWithName(method.Name)
|| (method.IsStatic ? !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) : !(target.Expression is ThisReferenceExpression)); || (method.IsStatic ? !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) : !(target.Expression is ThisReferenceExpression));
} }

22
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1669,7 +1669,7 @@ namespace ICSharpCode.Decompiler.CSharp
} }
internal TranslatedExpression TranslateTarget(ILInstruction target, bool nonVirtualInvocation, internal TranslatedExpression TranslateTarget(ILInstruction target, bool nonVirtualInvocation,
bool memberStatic, IType memberDeclaringType) bool memberStatic, IType memberDeclaringType, bool unwrapBoxingConversion = false)
{ {
// If references are missing member.IsStatic might not be set correctly. // If references are missing member.IsStatic might not be set correctly.
// Additionally check target for null, in order to avoid a crash. // Additionally check target for null, in order to avoid a crash.
@ -1680,9 +1680,15 @@ namespace ICSharpCode.Decompiler.CSharp
.WithRR(new ThisResolveResult(memberDeclaringType, nonVirtualInvocation)); .WithRR(new ThisResolveResult(memberDeclaringType, nonVirtualInvocation));
} else { } else {
var translatedTarget = Translate(target, memberDeclaringType); var translatedTarget = Translate(target, memberDeclaringType);
if (CallInstruction.ExpectedTypeForThisPointer(memberDeclaringType) == StackType.Ref && translatedTarget.Type.GetStackType().IsIntegerType()) { if (unwrapBoxingConversion) {
// when accessing members on value types, ensure we use a reference and not a pointer translatedTarget = ExpressionBuilder.UnwrapBoxingConversion(translatedTarget);
translatedTarget = translatedTarget.ConvertTo(new ByReferenceType(memberDeclaringType), this); }
if (CallInstruction.ExpectedTypeForThisPointer(memberDeclaringType) == StackType.Ref) {
// When accessing members on value types, ensure we use a reference of the correct type,
// and not a pointer or a reference to a different type (issue #1333)
if (!(translatedTarget.Type is ByReferenceType brt && NormalizeTypeVisitor.TypeErasure.EquivalentTypes(brt.ElementType, memberDeclaringType))) {
translatedTarget = translatedTarget.ConvertTo(new ByReferenceType(memberDeclaringType), this);
}
} }
if (translatedTarget.Expression is DirectionExpression) { if (translatedTarget.Expression is DirectionExpression) {
// (ref x).member => x.member // (ref x).member => x.member
@ -2386,8 +2392,14 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitAddressOf(AddressOf inst, TranslationContext context) protected internal override TranslatedExpression VisitAddressOf(AddressOf inst, TranslationContext context)
{ {
IType targetTypeHint = null;
if (context.TypeHint is ByReferenceType brt) {
targetTypeHint = brt.ElementType;
} else if (context.TypeHint is PointerType pt) {
targetTypeHint = pt.ElementType;
}
// HACK: this is only correct if the argument is an R-value; otherwise we're missing the copy to the temporary // HACK: this is only correct if the argument is an R-value; otherwise we're missing the copy to the temporary
var value = Translate(inst.Value); var value = Translate(inst.Value, targetTypeHint);
return new DirectionExpression(FieldDirection.Ref, value) return new DirectionExpression(FieldDirection.Ref, value)
.WithILInstruction(inst) .WithILInstruction(inst)
.WithRR(new ByReferenceResolveResult(value.ResolveResult, false)); .WithRR(new ByReferenceResolveResult(value.ResolveResult, false));

11
ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs

@ -348,9 +348,18 @@ namespace ICSharpCode.Decompiler.CSharp
return pointerExpr.ConvertTo(targetType, expressionBuilder); return pointerExpr.ConvertTo(targetType, expressionBuilder);
} }
if (targetType.Kind == TypeKind.ByReference) { if (targetType.Kind == TypeKind.ByReference) {
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()) {
// When converting a reference to a temporary to a different type,
// apply the cast to the temporary instead.
var convertedTemp = this.UnwrapChild(thisDir.Expression).ConvertTo(elementType, expressionBuilder, checkForOverflow);
return new DirectionExpression(FieldDirection.Ref, convertedTemp)
.WithILInstruction(this.ILInstructions)
.WithRR(new ByReferenceResolveResult(convertedTemp.ResolveResult, false));
}
// Convert from integer/pointer to reference. // Convert from integer/pointer to reference.
// First, convert to the corresponding pointer type: // First, convert to the corresponding pointer type:
var elementType = ((ByReferenceType)targetType).ElementType;
var arg = this.ConvertTo(new PointerType(elementType), expressionBuilder, checkForOverflow); var arg = this.ConvertTo(new PointerType(elementType), expressionBuilder, checkForOverflow);
Expression expr; Expression expr;
ResolveResult elementRR; ResolveResult elementRR;

Loading…
Cancel
Save