Browse Source

Fix #1389: Translation of "isinst" was incorrect for value types was causing an assertion.

pull/1423/head
Daniel Grunwald 7 years ago
parent
commit
2eafa0c695
  1. 32
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  2. 16
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

32
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -265,6 +265,25 @@ namespace ICSharpCode.Decompiler.CSharp @@ -265,6 +265,25 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitIsInst(IsInst inst, TranslationContext context)
{
var arg = Translate(inst.Argument);
if (inst.Type.IsReferenceType == false) {
// isinst with a value type results in an expression of "boxed value type",
// which is not supported in C#.
// Note that several other instructions special-case isinst arguments:
// unbox.any T(isinst T(expr)) ==> "expr as T" for nullable value types
// comp(isinst T(expr) != null) ==> "expr is T"
// on block level (StatementBuilder.VisitIsInst) => "expr is T"
if (SemanticHelper.IsPure(inst.Argument.Flags)) {
// We can emulate isinst using
// expr is T ? expr : null
return new ConditionalExpression(
new IsExpression(arg, ConvertType(inst.Type)).WithILInstruction(inst),
arg.Expression.Clone(),
new NullReferenceExpression()
).WithoutILInstruction().WithRR(new ResolveResult(arg.Type));
} else {
return ErrorExpression("isinst with value type is only supported in some contexts");
}
}
arg = UnwrapBoxingConversion(arg);
return new AsExpression(arg.Expression, ConvertType(inst.Type))
.WithILInstruction(inst)
@ -1948,12 +1967,17 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1948,12 +1967,17 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitUnboxAny(UnboxAny inst, TranslationContext context)
{
var arg = Translate(inst.Argument);
if (arg.Type.Equals(inst.Type) && inst.Argument.OpCode == OpCode.IsInst) {
// isinst followed by unbox.any of the same type is used for as-casts to generic types
return arg.WithILInstruction(inst);
TranslatedExpression arg;
if (inst.Argument is IsInst isInst && inst.Type.Equals(isInst.Type)) {
// unbox.any T(isinst T(expr)) ==> expr as T
// This is used for generic types and nullable value types
arg = UnwrapBoxingConversion(Translate(isInst.Argument));
return new AsExpression(arg, ConvertType(inst.Type))
.WithILInstruction(inst)
.WithRR(new ConversionResolveResult(inst.Type, arg.ResolveResult, Conversion.TryCast));
}
arg = Translate(inst.Argument);
IType targetType = inst.Type;
if (targetType.Kind == TypeKind.TypeParameter) {
var rr = resolver.ResolveCast(targetType, arg.ResolveResult);

16
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -66,6 +66,22 @@ namespace ICSharpCode.Decompiler.CSharp @@ -66,6 +66,22 @@ namespace ICSharpCode.Decompiler.CSharp
return new ExpressionStatement(exprBuilder.Translate(inst));
}
protected internal override Statement VisitIsInst(IsInst inst)
{
// isinst on top-level (unused result) can be translated in general
// (even for value types) by using "is" instead of "as"
// This can happen when the result of "expr is T" is unused
// and the C# compiler optimizes away the null check portion of the "is" operator.
return new ExpressionStatement(
new IsExpression(
exprBuilder.Translate(inst.Argument),
exprBuilder.ConvertType(inst.Type)
)
.WithRR(new ResolveResult(exprBuilder.compilation.FindType(KnownTypeCode.Boolean)))
.WithILInstruction(inst)
);
}
protected internal override Statement VisitStLoc(StLoc inst)
{
var expr = exprBuilder.Translate(inst);

Loading…
Cancel
Save