diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index c1d13a79b..197c27de7 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -602,6 +602,7 @@ namespace ICSharpCode.Decompiler.CSharp TranslatedExpression HandleCallInstruction(CallInstruction inst) { + IMethod method = inst.Method; // Used for Call, CallVirt and NewObj TranslatedExpression target; if (inst.OpCode == OpCode.NewObj) { @@ -610,30 +611,41 @@ namespace ICSharpCode.Decompiler.CSharp } target = default(TranslatedExpression); // no target } else { - target = TranslateTarget(inst.Method, inst.Arguments.FirstOrDefault(), inst.OpCode == OpCode.Call); + target = TranslateTarget(method, inst.Arguments.FirstOrDefault(), inst.OpCode == OpCode.Call); } var arguments = inst.Arguments.SelectArray(Translate); - int firstParamIndex = (inst.Method.IsStatic || inst.OpCode == OpCode.NewObj) ? 0 : 1; + int firstParamIndex = (method.IsStatic || inst.OpCode == OpCode.NewObj) ? 0 : 1; // Translate arguments to the expected parameter types Debug.Assert(arguments.Length == firstParamIndex + inst.Method.Parameters.Count); for (int i = firstParamIndex; i < arguments.Length; i++) { - var parameter = inst.Method.Parameters[i - firstParamIndex]; + var parameter = method.Parameters[i - firstParamIndex]; arguments[i] = arguments[i].ConvertTo(parameter.Type, this); if (parameter.IsOut && arguments[i].Expression is DirectionExpression) { ((DirectionExpression)arguments[i].Expression).FieldDirection = FieldDirection.Out; } } + + if (method is VarArgInstanceMethod) { + int regularParameterCount = ((VarArgInstanceMethod)method).RegularParameterCount; + var argListArg = new UndocumentedExpression(); + argListArg.UndocumentedExpressionType = UndocumentedExpressionType.ArgList; + argListArg.Arguments.AddRange(arguments.Skip(regularParameterCount).Select(arg => arg.Expression)); + var argListRR = new ResolveResult(SpecialType.ArgList); + arguments = arguments.Take(regularParameterCount) + .Concat(new[] { argListArg.WithoutILInstruction().WithRR(argListRR) }).ToArray(); + method = (IMethod)method.MemberDefinition; + } var argumentResolveResults = arguments.Skip(firstParamIndex).Select(arg => arg.ResolveResult).ToList(); ResolveResult rr; if (inst.Method.IsAccessor) - rr = new MemberResolveResult(target.ResolveResult, inst.Method.AccessorOwner); + rr = new MemberResolveResult(target.ResolveResult, method.AccessorOwner); else - rr = new CSharpInvocationResolveResult(target.ResolveResult, inst.Method, argumentResolveResults); + rr = new CSharpInvocationResolveResult(target.ResolveResult, method, argumentResolveResults); var argumentExpressions = arguments.Skip(firstParamIndex).Select(arg => arg.Expression).ToList(); if (inst.OpCode == OpCode.NewObj) { @@ -641,7 +653,6 @@ namespace ICSharpCode.Decompiler.CSharp .WithILInstruction(inst).WithRR(rr); } else { Expression expr; - IMethod method = inst.Method; if (method.IsAccessor) { if (method.ReturnType.IsKnownType(KnownTypeCode.Void)) { var value = argumentExpressions.Last(); @@ -669,14 +680,14 @@ namespace ICSharpCode.Decompiler.CSharp } } else { Expression targetExpr = target.Expression; - string methodName = inst.Method.Name; + string methodName = method.Name; // HACK : convert this.Dispose() to ((IDisposable)this).Dispose(), if Dispose is an explicitly implemented interface method. if (inst.Method.IsExplicitInterfaceImplementation && targetExpr is ThisReferenceExpression) { - targetExpr = targetExpr.CastTo(ConvertType(inst.Method.ImplementedInterfaceMembers[0].DeclaringType)); - methodName = inst.Method.ImplementedInterfaceMembers[0].Name; + targetExpr = targetExpr.CastTo(ConvertType(method.ImplementedInterfaceMembers[0].DeclaringType)); + methodName = method.ImplementedInterfaceMembers[0].Name; } var mre = new MemberReferenceExpression(targetExpr, methodName); - mre.TypeArguments.AddRange(inst.Method.TypeArguments.Select(a => ConvertType(a))); + mre.TypeArguments.AddRange(method.TypeArguments.Select(a => ConvertType(a))); expr = new InvocationExpression(mre, argumentExpressions); } return expr.WithILInstruction(inst).WithRR(rr); @@ -869,7 +880,7 @@ namespace ICSharpCode.Decompiler.CSharp var expr = new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.RefValue, Arguments = { Translate(inst.Argument).Expression, new TypeReferenceExpression(ConvertType(inst.Type)) } - }; + }.WithRR(new ResolveResult(inst.Type)); return new DirectionExpression(FieldDirection.Ref, expr.WithILInstruction(inst)).WithoutILInstruction() .WithRR(new ByReferenceResolveResult(inst.Type, false)); } diff --git a/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs b/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs index b447d666a..b66ee38f1 100644 --- a/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs +++ b/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs @@ -129,6 +129,9 @@ namespace ICSharpCode.Decompiler.Disassembler } else if (method.HasThis) { writer.Write("instance "); } + if (method.CallingConvention == MethodCallingConvention.VarArg) { + writer.Write("vararg "); + } method.ReturnType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters); writer.Write(' '); if (method.DeclaringType != null) { @@ -154,7 +157,8 @@ namespace ICSharpCode.Decompiler.Disassembler writer.Write("("); var parameters = method.Parameters; for (int i = 0; i < parameters.Count; ++i) { - if (i > 0) writer.Write(", "); + if (i > 0) + writer.Write(", "); parameters[i].ParameterType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters); } writer.Write(")"); @@ -285,6 +289,9 @@ namespace ICSharpCode.Decompiler.Disassembler writer.Write(" modreq("); ((RequiredModifierType)type).ModifierType.WriteTo(writer, ILNameSyntax.TypeName); writer.Write(") "); + } else if (type is SentinelType) { + writer.Write("..., "); + ((SentinelType)type).ElementType.WriteTo(writer, syntax); } else { string name = PrimitiveTypeName(type.FullName); if (syntax == ILNameSyntax.ShortTypeName) { diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 5f257c5de..b88fe3d55 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -158,6 +158,7 @@ + diff --git a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj index 69365d5d6..a25b2fc71 100644 --- a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj @@ -125,6 +125,7 @@ + diff --git a/ICSharpCode.Decompiler/Tests/UndocumentedExpressions.cs b/ICSharpCode.Decompiler/Tests/TestCases/UndocumentedExpressions.cs similarity index 69% rename from ICSharpCode.Decompiler/Tests/UndocumentedExpressions.cs rename to ICSharpCode.Decompiler/Tests/TestCases/UndocumentedExpressions.cs index 80f5b0371..3d3837eeb 100644 --- a/ICSharpCode.Decompiler/Tests/UndocumentedExpressions.cs +++ b/ICSharpCode.Decompiler/Tests/TestCases/UndocumentedExpressions.cs @@ -20,10 +20,34 @@ using System; public class UndocumentedExpressions { - public static int GetArgCount(__arglist) + static void Main(string[] args) + { + MakeTypedRef("abc"); + VarArgs(1, __arglist()); + VarArgs(__arglist(1)); + VarArgs(1, __arglist("abc", 2, true)); + } + + public static void VarArgs(int normalArg, __arglist) { ArgIterator argIterator = new ArgIterator(__arglist); - return argIterator.GetRemainingCount(); + Console.WriteLine("Called with {0} arguments", argIterator.GetRemainingCount()); + int pos = 0; + while (argIterator.GetRemainingCount() > 0) { + TypedReference tr = argIterator.GetNextArg(); + object val; + try { + val = __refvalue(tr, object); + } catch (Exception ex) { + val = ex.GetType().Name; + } + Console.WriteLine("{0} : {1} = {2}", pos++, __reftype(tr).Name, val); + } + } + + public static void VarArgs(__arglist) + { + Console.WriteLine("The other varargs overload"); } public static void MakeTypedRef(object o) diff --git a/ICSharpCode.Decompiler/Tests/TestRunner.cs b/ICSharpCode.Decompiler/Tests/TestRunner.cs index 2ac194e57..bbd3debf0 100644 --- a/ICSharpCode.Decompiler/Tests/TestRunner.cs +++ b/ICSharpCode.Decompiler/Tests/TestRunner.cs @@ -93,6 +93,12 @@ namespace ICSharpCode.Decompiler.Tests { TestCompileDecompileCompileOutputAll("DecimalFields.cs"); } + + [Test] + public void UndocumentedExpressions() + { + TestCompileDecompileCompileOutputAll("UndocumentedExpressions.cs"); + } void TestCompileDecompileCompileOutputAll(string testFileName) { @@ -138,6 +144,10 @@ namespace ICSharpCode.Decompiler.Tests Assert.AreEqual(result1, result2, "Exit codes differ; did the decompiled code crash?"); Assert.AreEqual(error1, error2); Assert.AreEqual(output1, output2); + + File.Delete(decompiledCodeFile); + File.Delete(outputFile.PathToAssembly); + File.Delete(decompiledOutputFile.PathToAssembly); } finally { if (outputFile != null) outputFile.TempFiles.Delete(); diff --git a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs index 6431cadc0..65fc7be55 100644 --- a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs +++ b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs @@ -120,6 +120,9 @@ namespace ICSharpCode.Decompiler { if (typeReference == null) return SpecialType.UnknownType; + if (typeReference is SentinelType) { + typeReference = ((SentinelType)typeReference).ElementType; + } ITypeReference typeRef; if (typeReference is PinnedType) { typeRef = new NRefactory.TypeSystem.ByReferenceType(Resolve(((PinnedType)typeReference).ElementType)).ToTypeReference(); @@ -197,7 +200,12 @@ namespace ICSharpCode.Decompiler IMethod method; if (!methodLookupCache.TryGetValue(methodReference, out method)) { method = FindNonGenericMethod(methodReference.GetElementMethod()); - if (methodReference.IsGenericInstance || methodReference.DeclaringType.IsGenericInstance) { + if (methodReference.CallingConvention == MethodCallingConvention.VarArg) { + method = new VarArgInstanceMethod( + method, + methodReference.Parameters.SkipWhile(p => !p.ParameterType.IsSentinel).Select(p => Resolve(p.ParameterType)) + ); + } else if (methodReference.IsGenericInstance || methodReference.DeclaringType.IsGenericInstance) { IList classTypeArguments = null; IList methodTypeArguments = null; if (methodReference.IsGenericInstance) { @@ -234,7 +242,16 @@ namespace ICSharpCode.Decompiler if (GetCecil(method) == methodReference) return method; } - var parameterTypes = methodReference.Parameters.SelectArray(p => Resolve(p.ParameterType)); + IType[] parameterTypes; + if (methodReference.CallingConvention == MethodCallingConvention.VarArg) { + parameterTypes = methodReference.Parameters + .TakeWhile(p => !p.ParameterType.IsSentinel) + .Select(p => Resolve(p.ParameterType)) + .Concat(new[] { SpecialType.ArgList }) + .ToArray(); + } else { + parameterTypes = methodReference.Parameters.SelectArray(p => Resolve(p.ParameterType)); + } var returnType = Resolve(methodReference.ReturnType); foreach (var method in methods) { if (method.TypeParameters.Count != methodReference.GenericParameters.Count) @@ -245,7 +262,7 @@ namespace ICSharpCode.Decompiler } return CreateFakeMethod(methodReference); } - + static bool CompareTypes(IType a, IType b) { IType type1 = DummyTypeParameter.NormalizeAllTypeParameters(a); @@ -253,6 +270,11 @@ namespace ICSharpCode.Decompiler return type1.Equals(type2); } + static bool IsVarArgMethod(IMethod method) + { + return method.Parameters.Count > 0 && method.Parameters[method.Parameters.Count - 1].Type.Kind == TypeKind.ArgList; + } + static bool CompareSignatures(IList parameters, IType[] parameterTypes) { if (parameterTypes.Length != parameters.Count) diff --git a/ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs b/ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs new file mode 100644 index 000000000..61183a2c2 --- /dev/null +++ b/ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs @@ -0,0 +1,356 @@ +// Copyright (c) 2016 Daniel Grunwald +// +// 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 System.Diagnostics; +using System.Linq; +using System.Text; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; + +namespace ICSharpCode.Decompiler +{ + /// + /// Used when calling a vararg method. Stores the actual parameter types being passed. + /// + public class VarArgInstanceMethod : IMethod + { + readonly IMethod baseMethod; + readonly IParameter[] parameters; + + public VarArgInstanceMethod(IMethod baseMethod, IEnumerable varArgTypes) + { + this.baseMethod = baseMethod; + var paramList = new List(baseMethod.Parameters); + Debug.Assert(paramList.Last().Type.Kind == TypeKind.ArgList); + paramList.RemoveAt(paramList.Count - 1); + foreach (IType varArg in varArgTypes) { + paramList.Add(new DefaultParameter(varArg, name: string.Empty, owner: this)); + } + this.parameters = paramList.ToArray(); + } + + public int RegularParameterCount { + get { return baseMethod.Parameters.Count - 1; } + } + + public IList Parameters { + get { return parameters; } + } + + public override bool Equals(object obj) + { + VarArgInstanceMethod other = obj as VarArgInstanceMethod; + return other != null && baseMethod.Equals(other.baseMethod); + } + + public override int GetHashCode() + { + return baseMethod.GetHashCode(); + } + + public override string ToString() + { + StringBuilder b = new StringBuilder("["); + b.Append(this.SymbolKind); + if (this.DeclaringType != null) { + b.Append(this.DeclaringType.ReflectionName); + b.Append('.'); + } + b.Append(this.Name); + if (this.TypeParameters.Count > 0) { + b.Append("``"); + b.Append(this.TypeParameters.Count); + } + b.Append('('); + for (int i = 0; i < this.Parameters.Count; i++) { + if (i > 0) + b.Append(", "); + if (i == this.RegularParameterCount) + b.Append("..., "); + b.Append(this.Parameters[i].Type.ReflectionName); + } + if (this.Parameters.Count == this.RegularParameterCount) { + b.Append(", ..."); + } + b.Append("):"); + b.Append(this.ReturnType.ReflectionName); + b.Append(']'); + return b.ToString(); + } + + #region IMethod implementation + public IMethod Specialize(TypeParameterSubstitution substitution) + { + return new VarArgInstanceMethod( + baseMethod.Specialize(substitution), + parameters.Skip(baseMethod.Parameters.Count - 1).Select(p => p.Type.AcceptVisitor(substitution)).ToList()); + } + + public IList Parts { + get { return baseMethod.Parts; } + } + + public IList ReturnTypeAttributes { + get { return baseMethod.ReturnTypeAttributes; } + } + + public IList TypeParameters { + get { return baseMethod.TypeParameters; } + } + + public bool IsParameterized { + get { return baseMethod.IsParameterized; } + } + + public IList TypeArguments { + get { return baseMethod.TypeArguments; } + } + + public bool IsExtensionMethod { + get { return baseMethod.IsExtensionMethod; } + } + + public bool IsConstructor { + get { return baseMethod.IsConstructor; } + } + + public bool IsDestructor { + get { return baseMethod.IsDestructor; } + } + + public bool IsOperator { + get { return baseMethod.IsOperator; } + } + + public bool IsPartial { + get { return baseMethod.IsPartial; } + } + + public bool IsAsync { + get { return baseMethod.IsAsync; } + } + + public bool HasBody { + get { return baseMethod.HasBody; } + } + + public bool IsAccessor { + get { return baseMethod.IsAccessor; } + } + + public IMember AccessorOwner { + get { return baseMethod.AccessorOwner; } + } + + public IMethod ReducedFrom { + get { return baseMethod.ReducedFrom; } + } + + #endregion + + #region IMember implementation + + [Obsolete("Use the ToReference method instead.")] + public IMemberReference ToMemberReference() + { + return ToReference(); + } + + public IMemberReference ToReference() + { + throw new NotImplementedException(); + } + + IMember IMember.Specialize(TypeParameterSubstitution substitution) + { + return Specialize(substitution); + } + + public IMember MemberDefinition { + get { return baseMethod.MemberDefinition; } + } + + public IUnresolvedMember UnresolvedMember { + get { return baseMethod.UnresolvedMember; } + } + + public IType ReturnType { + get { return baseMethod.ReturnType; } + } + + public IList ImplementedInterfaceMembers { + get { return baseMethod.ImplementedInterfaceMembers; } + } + + public bool IsExplicitInterfaceImplementation { + get { return baseMethod.IsExplicitInterfaceImplementation; } + } + + public bool IsVirtual { + get { return baseMethod.IsVirtual; } + } + + public bool IsOverride { + get { return baseMethod.IsOverride; } + } + + public bool IsOverridable { + get { return baseMethod.IsOverridable; } + } + + public TypeParameterSubstitution Substitution { + get { return baseMethod.Substitution; } + } + + #endregion + + #region ISymbol implementation + + ISymbolReference ISymbol.ToReference() + { + return ToReference(); + } + + public SymbolKind SymbolKind { + get { return baseMethod.SymbolKind; } + } + + public string Name { + get { return baseMethod.Name; } + } + + #endregion + + #region IEntity implementation + + [Obsolete] + public EntityType EntityType { + get { + throw new NotImplementedException(); + } + } + + public DomRegion Region { + get { return baseMethod.Region; } + } + + public DomRegion BodyRegion { + get { return baseMethod.BodyRegion; } + } + + public ITypeDefinition DeclaringTypeDefinition { + get { return baseMethod.DeclaringTypeDefinition; } + } + + public IType DeclaringType { + get { return baseMethod.DeclaringType; } + } + + public IAssembly ParentAssembly { + get { return baseMethod.ParentAssembly; } + } + + public IList Attributes { + get { return baseMethod.Attributes; } + } + + public ICSharpCode.NRefactory.Documentation.DocumentationComment Documentation { + get { return baseMethod.Documentation; } + } + + public bool IsStatic { + get { return baseMethod.IsStatic; } + } + + public bool IsAbstract { + get { return baseMethod.IsAbstract; } + } + + public bool IsSealed { + get { return baseMethod.IsSealed; } + } + + public bool IsShadowing { + get { return baseMethod.IsShadowing; } + } + + public bool IsSynthetic { + get { return baseMethod.IsSynthetic; } + } + + #endregion + + #region IHasAccessibility implementation + + public Accessibility Accessibility { + get { return baseMethod.Accessibility; } + } + + public bool IsPrivate { + get { return baseMethod.IsPrivate; } + } + + public bool IsPublic { + get { return baseMethod.IsPublic; } + } + + public bool IsProtected { + get { return baseMethod.IsProtected; } + } + + public bool IsInternal { + get { return baseMethod.IsInternal; } + } + + public bool IsProtectedOrInternal { + get { return baseMethod.IsProtectedOrInternal; } + } + + public bool IsProtectedAndInternal { + get { return baseMethod.IsProtectedAndInternal; } + } + + #endregion + + #region INamedElement implementation + + public string FullName { + get { return baseMethod.FullName; } + } + + public string ReflectionName { + get { return baseMethod.ReflectionName; } + } + + public string Namespace { + get { return baseMethod.Namespace; } + } + + #endregion + + #region ICompilationProvider implementation + + public ICompilation Compilation { + get { return baseMethod.Compilation; } + } + + #endregion + } +} diff --git a/NRefactory b/NRefactory index 43306b891..8369bc77d 160000 --- a/NRefactory +++ b/NRefactory @@ -1 +1 @@ -Subproject commit 43306b8912c6806f3c19d8a288ddeeaca07df5d2 +Subproject commit 8369bc77d14701792c05fa6bb50f7d299072041b