Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Range syntax in combination with ?[] does not work as expected #2156

Closed
mostevercxz opened this issue Sep 11, 2020 · 2 comments
Closed

Range syntax in combination with ?[] does not work as expected #2156

mostevercxz opened this issue Sep 11, 2020 · 2 comments
Labels
Decompiler The decompiler engine itself Enhancement Areas for improvement

Comments

@mostevercxz
Copy link

Input code

using System;
using System.IO;

namespace switchTest
{
    static class TestSwitch
    {
        public static string Create(string url)
        {
            string ext = url.Contains('.') ? Path.GetExtension(url)?[1..] : url;
            // The following code works correctly
            //string ext = url;
            string ret;
            switch (ext)
            {
                case "dog": ret = "1"; break;
                case "fish":
                case "cat": ret = "2"; break;
                default: throw new Exception($"unsupported:{url}");
            }
            return ret;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var ani = TestSwitch.Create(args[0]);
            Console.WriteLine(ani);
        }
    }
}

Erroneous output

Error decompiling @06000001 switchTest.TestSwitch.Create
in assembly "D:\bin\Release\netcoreapp3.1\switchTest.dll"
 ---> System.InvalidCastException: Cast from Int64 to Object not supported.
   at ICSharpCode.Decompiler.Util.CSharpPrimitiveCast.CSharpPrimitiveCastUnchecked(TypeCode targetType, Object input) in CSharpPrimitiveCast.cs:line 769
   at ICSharpCode.Decompiler.Util.CSharpPrimitiveCast.Cast(TypeCode targetType, Object input, Boolean checkForOverflow) in CSharpPrimitiveCast.cs:line 44
   at ICSharpCode.Decompiler.CSharp.StatementBuilder.<CreateTypedCaseLabel>d__17.MoveNext() in StatementBuilder.cs:line 176
   at System.Linq.Enumerable.<SelectManyIterator>d__17`2.MoveNext() in offset 138
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source) in offset 114
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source) in offset 20
   at ICSharpCode.Decompiler.CSharp.StatementBuilder.TranslateSwitch(BlockContainer switchContainer, SwitchInstruction inst) in StatementBuilder.cs:line 227
   at ICSharpCode.Decompiler.CSharp.StatementBuilder.VisitBlockContainer(BlockContainer container) in StatementBuilder.cs:line 1139
   at ICSharpCode.Decompiler.IL.BlockContainer.AcceptVisitor[T](ILVisitor`1 visitor) in Instructions.cs:line 851
   at ICSharpCode.Decompiler.CSharp.StatementBuilder.Convert(ILInstruction inst) in StatementBuilder.cs:line 78
   at ICSharpCode.Decompiler.CSharp.StatementBuilder.ConvertBlockContainer(BlockStatement blockStatement, BlockContainer container, IEnumerable`1 blocks, Boolean isLoop) in StatementBuilder.cs:line 1317
   at ICSharpCode.Decompiler.CSharp.StatementBuilder.ConvertBlockContainer(BlockContainer container, Boolean isLoop) in StatementBuilder.cs:line 1257
   at ICSharpCode.Decompiler.CSharp.StatementBuilder.VisitBlockContainer(BlockContainer container) in StatementBuilder.cs:line 1143
   at ICSharpCode.Decompiler.IL.BlockContainer.AcceptVisitor[T](ILVisitor`1 visitor) in Instructions.cs:line 851
   at ICSharpCode.Decompiler.CSharp.StatementBuilder.Convert(ILInstruction inst) in StatementBuilder.cs:line 78
   at ICSharpCode.Decompiler.CSharp.StatementBuilder.ConvertAsBlock(ILInstruction inst) in StatementBuilder.cs:line 83
   at ICSharpCode.Decompiler.CSharp.CSharpDecompiler.DecompileBody(IMethod method, EntityDeclaration entityDecl, DecompileRun decompileRun, ITypeResolveContext decompilationContext) in CSharpDecompiler.cs:line 1467
-- continuing with outer exception (ICSharpCode.Decompiler.DecompilerException) --
   at ICSharpCode.Decompiler.CSharp.CSharpDecompiler.DecompileBody(IMethod method, EntityDeclaration entityDecl, DecompileRun decompileRun, ITypeResolveContext decompilationContext) in CSharpDecompiler.cs:line 1483
   at ICSharpCode.Decompiler.CSharp.CSharpDecompiler.DoDecompile(IMethod method, DecompileRun decompileRun, ITypeResolveContext decompilationContext) in CSharpDecompiler.cs:line 1374
   at ICSharpCode.Decompiler.CSharp.CSharpDecompiler.DoDecompile(ITypeDefinition typeDef, DecompileRun decompileRun, ITypeResolveContext decompilationContext) in CSharpDecompiler.cs:line 1212
   at ICSharpCode.Decompiler.CSharp.CSharpDecompiler.Decompile(IEnumerable`1 definitions) in CSharpDecompiler.cs:line 938
   at ICSharpCode.Decompiler.CSharp.CSharpDecompiler.Decompile(EntityHandle[] definitions) in CSharpDecompiler.cs:line 907
   at ICSharpCode.ILSpy.CSharpLanguage.DecompileType(ITypeDefinition type, ITextOutput output, DecompilationOptions options) in CSharpLanguage.cs:line 322
   at ICSharpCode.ILSpy.TreeNodes.TypeTreeNode.Decompile(Language language, ITextOutput output, DecompilationOptions options) in TypeTreeNode.cs:line 123
   at ICSharpCode.ILSpy.TextView.DecompilerTextView.DecompileNodes(DecompilationContext context, ITextOutput textOutput) in DecompilerTextView.cs:line 881
   at ICSharpCode.ILSpy.TextView.DecompilerTextView.<>c__DisplayClass48_0.<DecompileAsync>b__0() in DecompilerTextView.cs:line 851

Details

  • Product in use: The lastest ILSpy github source code (2020-09-11)
  • Version in use: vs2019, F5 run ILSpy

Try to fix

else if (type.IsKnownType(KnownTypeCode.String) && map != null)

ILSpy will decompile without exception if you change line 151 to the following line:

} else if ((type.IsKnownType(KnownTypeCode.String) || type.IsKnownType(KnownTypeCode.Object)) && map != null) {

Besides, if you uncomment the following line in Input Code, ILSpy will work correctly:

// The following code works correctly
//string ext = url;
@siegfriedpammer
Copy link
Member

Current decompiler output:

// switchTest.TestSwitch
using System;
using System.IO;

public static string Create(string url)
{
	object obj;
	if (!url.Contains('.'))
	{
		obj = url;
	}
	else
	{
		string? extension = Path.GetExtension(url);
		obj = extension?[new Range(end: extension!.Length, start: 1)];
	}
	switch ((string)obj)
	{
	case "dog":
		return "1";
	case "fish":
	case "cat":
		return "2";
	default:
		throw new Exception("unsupported:" + url);
	}
}

Two/three things that should be improved:

  1. obj should be typed either string or string?
  2. range syntax detection should work
  3. possibly use ?: instead of an if-statement

@siegfriedpammer siegfriedpammer changed the title string switch fails to decompile Range syntax in combination with ?[] does not work as expected Sep 11, 2020
@siegfriedpammer siegfriedpammer added Enhancement Areas for improvement and removed Bug labels Sep 11, 2020
@dgrunwald
Copy link
Member

dgrunwald commented Sep 11, 2020

obj should be typed either string or string?

This is a bit tricky. For single-assignment stack slots we can use the C# type of the expression being assigned, but that approach won't work for multiple assignments.
Maybe add a ILAst-level type inference step?

range syntax detection should work

This is really the root cause of the issue.
If we can detect extension?[1..] then the second use of extension disappears.
That in turn allows us to inline extension, which makes the if simple enough so that ? : can be used, which then in turn allows us to inline obj.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Dec 11, 2020
ElektroKill added a commit to dnSpyEx/ILSpy that referenced this issue Aug 9, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Decompiler The decompiler engine itself Enhancement Areas for improvement
Projects
None yet
Development

No branches or pull requests

3 participants