Browse Source

Fix some issues with missing casts for overload resolution and for boxing in attributes.

pull/728/merge
Daniel Grunwald 9 years ago
parent
commit
f0a0ba8ac0
  1. 21
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  2. 27
      ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterOutputFormatter.cs
  3. 63
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  4. 52
      ICSharpCode.Decompiler/Tests/CallOverloadedMethod.cs
  5. 2
      ICSharpCode.Decompiler/Tests/CustomAttributes/CustomAttributeTests.cs
  6. 26
      ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributes.cs
  7. 45
      ICSharpCode.Decompiler/Tests/TestCases/Correctness/OverloadResolution.cs
  8. 2
      clean.bat
  9. 2
      debugbuild.bat
  10. 2
      releasebuild.bat

21
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -87,16 +87,17 @@ namespace ICSharpCode.Decompiler.CSharp @@ -87,16 +87,17 @@ namespace ICSharpCode.Decompiler.CSharp
public ExpressionWithResolveResult ConvertConstantValue(ResolveResult rr)
{
var expr = astBuilder.ConvertConstantValue(rr);
var pe = expr as PrimitiveExpression;
if (pe != null) {
if (pe.Value is sbyte)
expr = expr.CastTo(new PrimitiveType("sbyte"));
else if (pe.Value is byte)
expr = expr.CastTo(new PrimitiveType("byte"));
else if (pe.Value is short)
expr = expr.CastTo(new PrimitiveType("short"));
else if (pe.Value is ushort)
expr = expr.CastTo(new PrimitiveType("ushort"));
if (expr is NullReferenceExpression && rr.Type.Kind != TypeKind.Null) {
expr = expr.CastTo(ConvertType(rr.Type));
} else {
switch (rr.Type.GetDefinition()?.KnownTypeCode) {
case KnownTypeCode.SByte:
case KnownTypeCode.Byte:
case KnownTypeCode.Int16:
case KnownTypeCode.UInt16:
expr = expr.CastTo(new PrimitiveType(KnownTypeReference.GetCSharpNameByTypeCode(rr.Type.GetDefinition().KnownTypeCode)));
break;
}
}
var exprRR = expr.Annotation<ResolveResult>();
if (exprRR == null) {

27
ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterOutputFormatter.cs

@ -368,12 +368,31 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -368,12 +368,31 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
return "\\t";
case '\v':
return "\\v";
case ' ':
case '_':
case '`':
case '^':
// ASCII characters we allow directly in the output even though we don't use
// other Unicode characters of the same category.
return ch.ToString();
default:
if (char.IsControl(ch) || char.IsSurrogate(ch) ||
// print all uncommon white spaces as numbers
(char.IsWhiteSpace(ch) && ch != ' ')) {
switch (char.GetUnicodeCategory(ch)) {
case UnicodeCategory.ModifierLetter:
case UnicodeCategory.NonSpacingMark:
case UnicodeCategory.SpacingCombiningMark:
case UnicodeCategory.EnclosingMark:
case UnicodeCategory.LineSeparator:
case UnicodeCategory.ParagraphSeparator:
case UnicodeCategory.Control:
case UnicodeCategory.Format:
case UnicodeCategory.Surrogate:
case UnicodeCategory.PrivateUse:
case UnicodeCategory.ConnectorPunctuation:
case UnicodeCategory.ModifierSymbol:
case UnicodeCategory.OtherNotAssigned:
case UnicodeCategory.SpaceSeparator:
return "\\u" + ((int)ch).ToString("x4");
} else {
default:
return ch.ToString();
}
}

63
ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

@ -430,14 +430,23 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -430,14 +430,23 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
#endregion
#region Convert Constant Value
/// <summary>
/// Creates an Expression for the given constant value.
///
/// Note: the returned expression is not necessarily of the desired type.
/// However, the returned expression will always be implicitly convertible to <c>rr.Type</c>,
/// and will have the correct value when being converted in this way.
/// </summary>
public Expression ConvertConstantValue(ResolveResult rr)
{
if (rr == null)
throw new ArgumentNullException("rr");
if (rr is ConversionResolveResult) {
bool isBoxing = false;
if (rr is ConversionResolveResult crr) {
// unpack ConversionResolveResult if necessary
// (e.g. a boxing conversion or string->object reference conversion)
rr = ((ConversionResolveResult)rr).Input;
rr = crr.Input;
isBoxing = crr.Conversion.IsBoxingConversion;
}
if (rr is TypeOfResolveResult) {
@ -445,12 +454,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -445,12 +454,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
if (AddResolveResultAnnotations)
expr.AddAnnotation(rr);
return expr;
} else if (rr is ArrayCreateResolveResult) {
ArrayCreateResolveResult acrr = (ArrayCreateResolveResult)rr;
} else if (rr is ArrayCreateResolveResult acrr) {
ArrayCreateExpression ace = new ArrayCreateExpression();
ace.Type = ConvertType(acrr.Type);
ComposedType composedType = ace.Type as ComposedType;
if (composedType != null) {
if (ace.Type is ComposedType composedType) {
composedType.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers);
if (!composedType.HasNullableSpecifier && composedType.PointerRank == 0)
ace.Type = composedType.BaseType;
@ -469,12 +476,30 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -469,12 +476,30 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
ace.AddAnnotation(rr);
return ace;
} else if (rr.IsCompileTimeConstant) {
return ConvertConstantValue(rr.Type, rr.ConstantValue);
var expr = ConvertConstantValue(rr.Type, rr.ConstantValue);
if (isBoxing && IsSmallInteger(rr.Type)) {
// C# does not have small integer literal types.
// We need to add a cast so that the integer literal gets boxed as the correct type.
expr = new CastExpression(ConvertType(rr.Type), expr);
if (AddResolveResultAnnotations)
expr.AddAnnotation(rr);
}
return expr;
} else {
return new ErrorExpression();
}
}
/// <summary>
/// Creates an Expression for the given constant value.
///
/// Note: the returned expression is not necessarily of the specified <paramref name="type"/>:
/// For example, <c>ConvertConstantValue(typeof(string), null)</c> results in a <c>null</c> literal,
/// without a cast to <c>string</c>.
/// Similarly, <c>ConvertConstantValue(typeof(short), 1)</c> results in the literal <c>1</c>,
/// which is of type <c>int</c>.
/// However, the returned expression will always be implicitly convertible to <paramref name="type"/>.
/// </summary>
public Expression ConvertConstantValue(IType type, object constantValue)
{
if (type == null)
@ -494,7 +519,29 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -494,7 +519,29 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
} else if (type.Kind == TypeKind.Enum) {
return ConvertEnumValue(type, (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, constantValue, false));
} else {
return new PrimitiveExpression(constantValue);
if (IsSmallInteger(type)) {
// C# does not have integer literals of small integer types,
// use `int` literal instead.
constantValue = CSharpPrimitiveCast.Cast(TypeCode.Int32, constantValue, false);
type = type.GetDefinition().Compilation.FindType(KnownTypeCode.Int32);
}
var expr = new PrimitiveExpression(constantValue);
if (AddResolveResultAnnotations)
expr.AddAnnotation(new ConstantResolveResult(type, constantValue));
return expr;
}
}
bool IsSmallInteger(IType type)
{
switch (type.GetDefinition()?.KnownTypeCode) {
case KnownTypeCode.Byte:
case KnownTypeCode.SByte:
case KnownTypeCode.Int16:
case KnownTypeCode.UInt16:
return true;
default:
return false;
}
}

52
ICSharpCode.Decompiler/Tests/CallOverloadedMethod.cs

@ -1,52 +0,0 @@ @@ -1,52 +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;
using System.Collections.Generic;
public class CallOverloadedMethod
{
public void OverloadedMethod(object a)
{
}
public void OverloadedMethod(int? a)
{
}
public void OverloadedMethod(string a)
{
}
public void Call()
{
this.OverloadedMethod("(string)");
this.OverloadedMethod((object)"(object)");
this.OverloadedMethod(5);
this.OverloadedMethod((object)5);
this.OverloadedMethod(5L);
this.OverloadedMethod((object)null);
this.OverloadedMethod((string)null);
this.OverloadedMethod((int?)null);
}
public void CallMethodUsingInterface(List<int> list)
{
((ICollection<int>)list).Clear();
}
}

2
ICSharpCode.Decompiler/Tests/CustomAttributes/CustomAttributeTests.cs

@ -6,7 +6,7 @@ namespace ICSharpCode.Decompiler.Tests.CustomAttributes @@ -6,7 +6,7 @@ namespace ICSharpCode.Decompiler.Tests.CustomAttributes
public class CustomAttributeTests : DecompilerTestBase
{
[Test]
[Ignore("Needs event pattern detection, which waits for improved control flow detection (loop conditions)")]
[Ignore("Needs event pattern detection, which waits for improved control flow detection (do-while loops)")]
public void CustomAttributeSamples()
{
ValidateFileRoundtrip(@"CustomAttributes/S_CustomAttributeSamples.cs");

26
ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributes.cs

@ -63,7 +63,7 @@ namespace aa @@ -63,7 +63,7 @@ namespace aa
StringComparison.Ordinal,
StringComparison.CurrentCulture
})]
public static void ArrayAsAttribute1()
public static void EnumArray()
{
}
// Boxing of each array element
@ -71,7 +71,29 @@ namespace aa @@ -71,7 +71,29 @@ namespace aa
StringComparison.Ordinal,
StringComparison.CurrentCulture
})]
public static void ArrayAsAttribute2()
public static void BoxedEnumArray()
{
}
[My(new object[] {
1,
2u,
3L,
4uL,
// Ensure the decompiler doesn't leave these casts out:
(short)5,
(ushort)6,
(byte)7,
(sbyte)8,
'a',
'\0',
'\ufeff',
'\uffff',
1f,
2.0,
"text",
null
})]
public static void BoxedLiteralsArray()
{
}
}

45
ICSharpCode.Decompiler/Tests/TestCases/Correctness/OverloadResolution.cs

@ -28,15 +28,47 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -28,15 +28,47 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
{
static void Main()
{
CallOverloadedMethod();
TestBoxing();
CallIssue180();
CallExtensionMethod();
TestIssue180();
TestExtensionMethod();
}
#region Simple Overloaded Method
static void CallOverloadedMethod()
{
OverloadedMethod("(string)");
OverloadedMethod((object)"(object)");
OverloadedMethod(5);
OverloadedMethod((object)5);
OverloadedMethod(5L);
OverloadedMethod((object)null);
OverloadedMethod((string)null);
OverloadedMethod((int?)null);
}
static void OverloadedMethod(object a)
{
Console.WriteLine("OverloadedMethod(object={0}, object.GetType()={1})", a, a != null ? a.GetType().Name : "null");
}
static void OverloadedMethod(int? a)
{
Console.WriteLine("OverloadedMethod(int?={0})", a);
}
static void OverloadedMethod(string a)
{
Console.WriteLine("OverloadedMethod(string={0})", a);
}
#endregion
#region Boxing
static void TestBoxing()
{
Print(1);
Print((ushort)1);
Print(null);
}
static void Print(object obj)
@ -46,8 +78,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -46,8 +78,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
else
Console.WriteLine("{0}: {1}", obj.GetType().Name, obj);
}
#endregion
static void CallIssue180()
#region #180
static void TestIssue180()
{
Issue180(null);
Issue180(new object[1]);
@ -63,8 +97,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -63,8 +97,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
{
Console.WriteLine("#180: params object[]");
}
#endregion
static void CallExtensionMethod()
#region Extension Method
static void TestExtensionMethod()
{
new object().ExtensionMethod();
ExtensionMethod(null); // issue #167
@ -74,5 +110,6 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -74,5 +110,6 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
{
Console.WriteLine("ExtensionMethod(obj)");
}
#endregion
}
}

2
clean.bat

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
git submodule update --init || exit /b 1
)
@setlocal enabledelayedexpansion
set MSBUILD=
@set MSBUILD=
@for /D %%M in ("%ProgramFiles(x86)%\Microsoft Visual Studio\2017"\*) do (
@if exist "%%M\MSBuild\15.0\Bin\MSBuild.exe" (
@set "MSBUILD=%%M\MSBuild\15.0\Bin\MSBuild.exe"

2
debugbuild.bat

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
git submodule update --init || exit /b 1
)
@setlocal enabledelayedexpansion
set MSBUILD=
@set MSBUILD=
@for /D %%M in ("%ProgramFiles(x86)%\Microsoft Visual Studio\2017"\*) do (
@if exist "%%M\MSBuild\15.0\Bin\MSBuild.exe" (
@set "MSBUILD=%%M\MSBuild\15.0\Bin\MSBuild.exe"

2
releasebuild.bat

@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
git submodule update --init || exit /b 1
)
@setlocal enabledelayedexpansion
set MSBUILD=
@set MSBUILD=
@for /D %%M in ("%ProgramFiles(x86)%\Microsoft Visual Studio\2017"\*) do (
@if exist "%%M\MSBuild\15.0\Bin\MSBuild.exe" (
@set "MSBUILD=%%M\MSBuild\15.0\Bin\MSBuild.exe"

Loading…
Cancel
Save