Browse Source

Merge branch 'master' of https://github.com/icsharpcode/ILSpy into ldvirtdelegate

pull/1608/head
Siegfried Pammer 6 years ago
parent
commit
360fd68263
  1. 1
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  3. 14
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.cs
  4. 247
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ThrowExpressions.cs
  5. 10
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ValueTypes.cs
  6. 13
      ICSharpCode.Decompiler/CSharp/Annotations.cs
  7. 26
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  8. 3
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs
  9. 18
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  10. 16
      ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs
  11. 20
      ICSharpCode.Decompiler/DecompilerSettings.cs
  12. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  13. 2
      ICSharpCode.Decompiler/IL/Instructions.cs
  14. 2
      ICSharpCode.Decompiler/IL/Instructions.tt
  15. 2
      ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs
  16. 5
      ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs
  17. 11
      ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs
  18. 16
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  19. 82
      ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs
  20. 6
      ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs
  21. 2
      ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs
  22. 12
      ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs
  23. 22
      ICSharpCode.Decompiler/Semantics/Conversion.cs
  24. 29
      ICSharpCode.Decompiler/Semantics/ThrowResolveResult.cs
  25. 18
      ILSpy.BamlDecompiler/Rewrite/MarkupExtensionRewritePass.cs
  26. 9
      ILSpy/Properties/Resources.Designer.cs
  27. 3
      ILSpy/Properties/Resources.resx

1
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -82,6 +82,7 @@ @@ -82,6 +82,7 @@
<Compile Include="TestCases\ILPretty\ConstantBlobs.cs" />
<Compile Include="TestCases\Pretty\OutVariables.cs" />
<None Include="TestCases\VBPretty\VBCompoundAssign.cs" />
<Compile Include="TestCases\Pretty\ThrowExpressions.cs" />
<None Include="TestCases\ILPretty\Issue1145.cs" />
<Compile Include="TestCases\ILPretty\Issue1157.cs" />
<None Include="TestCases\ILPretty\Unsafe.cs" />

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -329,6 +329,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -329,6 +329,12 @@ namespace ICSharpCode.Decompiler.Tests
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void ThrowExpressions([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void WellKnownConstants([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
{

14
ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.cs

@ -25,6 +25,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -25,6 +25,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
private class MyClass
{
public int IntVal;
public readonly int ReadonlyIntVal;
public MyStruct StructField;
public readonly MyStruct ReadonlyStructField;
public string Text;
public MyClass Field;
public MyClass Property {
@ -45,6 +48,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -45,6 +48,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
private struct MyStruct
{
public int IntVal;
public readonly int ReadonlyIntVal;
public MyClass Field;
public MyStruct? Property1 => null;
public MyStruct Property2 => default(MyStruct);
@ -178,6 +182,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -178,6 +182,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
Use(GetMyClass()?.Text ?? "Hello");
}
public void CallOnValueTypeField()
{
Use(GetMyClass()?.IntVal.ToString());
Use(GetMyStruct()?.IntVal.ToString());
Use(GetMyClass()?.ReadonlyIntVal.ToString());
Use(GetMyStruct()?.ReadonlyIntVal.ToString());
GetMyClass()?.StructField.Done();
GetMyClass()?.ReadonlyStructField.Done();
}
public void InvokeDelegate(EventHandler eh)
{

247
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ThrowExpressions.cs

@ -0,0 +1,247 @@ @@ -0,0 +1,247 @@
using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
internal class ThrowExpressions
{
private class ArgumentCheckingCtor
{
private int initializedFromCtor = CountSheep() ?? throw new Exception("No sheep?!");
private object cacheObj = TryGetObj() ?? throw new Exception("What?");
private object simpleObj;
private int? nullableInt;
public ArgumentCheckingCtor(object simpleObj, int? nullableInt)
{
this.simpleObj = (simpleObj ?? throw new ArgumentNullException("simpleObj"));
this.nullableInt = (nullableInt ?? throw new ArgumentNullException("nullableInt"));
}
public ArgumentCheckingCtor(string input)
: this(input, GetIntOrNull(input ?? throw new ArgumentNullException("input")))
{
}
public ArgumentCheckingCtor(DataObject obj)
: this(obj ?? throw new Exception(), GetIntOrNull(obj.NullableDataField?.NullableDataField.ToString() ?? throw new ArgumentNullException("input")))
{
}
private static int? GetIntOrNull(string v)
{
if (int.TryParse(v, out int result)) {
return result;
}
return null;
}
private static int? CountSheep()
{
throw new NotImplementedException();
}
private static object TryGetObj()
{
return null;
}
public override int GetHashCode()
{
return initializedFromCtor;
}
public override bool Equals(object obj)
{
return true;
}
}
public class DataObject
{
public int IntField;
public int? NullableIntField;
public Data DataField;
public Data? NullableDataField;
public int IntProperty {
get;
set;
}
public int? NullableIntProperty {
get;
set;
}
public Data DataProperty {
get;
}
public Data? NullableDataProperty {
get;
}
}
public struct Data
{
public int IntField;
public int? NullableIntField;
public MoreData DataField;
public MoreData? NullableDataField;
public int IntProperty {
get;
set;
}
public int? NullableIntProperty {
get;
set;
}
public MoreData DataProperty {
get;
}
public MoreData? NullableDataProperty {
get;
}
}
public struct MoreData
{
public int IntField;
public int? NullableIntField;
public int IntProperty {
get;
set;
}
public int? NullableIntProperty {
get;
set;
}
}
public static int IntField;
public static int? NullableIntField;
public static object ObjectField;
public int InstIntField;
public int? InstNullableIntField;
public object InstObjectField;
public Data DataField;
public Data? NullableDataField;
public DataObject DataObjectField;
public static int IntProperty {
get;
}
public static int? NullableIntProperty {
get;
}
public static object ObjProperty {
get;
}
public int InstIntProperty {
get;
}
public int? InstNullableIntProperty {
get;
}
public object InstObjProperty {
get;
}
public Data DataProperty {
get;
}
public Data? NullableDataProperty {
get;
}
public DataObject DataObjectProperty {
get;
}
public static int ReturnIntField()
{
return NullableIntField ?? throw new Exception();
}
public static int ReturnIntProperty()
{
return NullableIntProperty ?? throw new Exception();
}
public static object ReturnObjField()
{
return ObjectField ?? throw new Exception();
}
public static object ReturnObjProperty()
{
return ObjProperty ?? throw new Exception();
}
public static int ReturnIntField(ThrowExpressions inst)
{
return inst.InstNullableIntField ?? throw new Exception();
}
public static int ReturnIntProperty(ThrowExpressions inst)
{
return inst.InstNullableIntProperty ?? throw new Exception();
}
public static object ReturnObjField(ThrowExpressions inst)
{
return inst.InstObjectField ?? throw new Exception();
}
public static object ReturnObjProperty(ThrowExpressions inst)
{
return inst.InstObjProperty ?? throw new Exception();
}
public static void UseComplexNullableStruct(ThrowExpressions inst)
{
Use(inst.InstNullableIntField ?? throw new Exception());
Use((inst.NullableDataField ?? throw new Exception()).IntField);
Use(inst.NullableDataField?.NullableIntField ?? throw new Exception());
Use((inst.NullableDataProperty ?? throw new Exception()).IntField);
Use(inst.NullableDataProperty?.NullableIntField ?? throw new Exception());
Use((inst.NullableDataField ?? throw new Exception()).DataField.IntField);
Use(inst.NullableDataField?.DataField.NullableIntField ?? throw new Exception());
Use((inst.NullableDataProperty ?? throw new Exception()).DataField.IntField);
Use(inst.NullableDataProperty?.DataField.NullableIntField ?? throw new Exception());
Use((inst.NullableDataField ?? throw new Exception()).DataProperty.IntField);
Use(inst.NullableDataField?.DataProperty.NullableIntField ?? throw new Exception());
Use((inst.NullableDataProperty ?? throw new Exception()).DataProperty.IntField);
Use(inst.NullableDataProperty?.DataProperty.NullableIntField ?? throw new Exception());
Use(inst.NullableDataField?.NullableDataField?.IntField ?? throw new Exception());
Use(inst.NullableDataField?.NullableDataField?.NullableIntField ?? throw new Exception());
Use(inst.NullableDataProperty?.NullableDataField?.IntField ?? throw new Exception());
Use(inst.NullableDataProperty?.NullableDataField?.NullableIntField ?? throw new Exception());
Use(inst.NullableDataField?.NullableDataProperty?.IntField ?? throw new Exception());
Use(inst.NullableDataField?.NullableDataProperty?.NullableIntField ?? throw new Exception());
Use(inst.NullableDataProperty?.NullableDataProperty?.IntField ?? throw new Exception());
Use(inst.NullableDataProperty?.NullableDataProperty?.NullableIntField ?? throw new Exception());
}
public static void UseComplexNullableObject(DataObject inst)
{
Use(inst?.NullableIntField ?? throw new Exception());
Use(inst?.NullableDataField?.IntField ?? throw new Exception());
Use(inst?.NullableDataField?.NullableIntField ?? throw new Exception());
Use(inst?.NullableDataProperty?.IntField ?? throw new Exception());
Use(inst?.NullableDataProperty?.NullableIntField ?? throw new Exception());
Use(inst?.NullableDataField?.DataField.IntField ?? throw new Exception());
Use(inst?.NullableDataField?.DataField.NullableIntField ?? throw new Exception());
Use(inst?.NullableDataProperty?.DataField.IntField ?? throw new Exception());
Use(inst?.NullableDataProperty?.DataField.NullableIntField ?? throw new Exception());
Use(inst?.NullableDataField?.DataProperty.IntField ?? throw new Exception());
Use(inst?.NullableDataField?.DataProperty.NullableIntField ?? throw new Exception());
Use(inst?.NullableDataProperty?.DataProperty.IntField ?? throw new Exception());
Use(inst?.NullableDataProperty?.DataProperty.NullableIntField ?? throw new Exception());
Use(inst?.NullableDataField?.NullableDataField?.IntField ?? throw new Exception());
Use(inst?.NullableDataField?.NullableDataField?.NullableIntField ?? throw new Exception());
Use(inst?.NullableDataProperty?.NullableDataField?.IntField ?? throw new Exception());
Use(inst?.NullableDataProperty?.NullableDataField?.NullableIntField ?? throw new Exception());
Use(inst?.NullableDataField?.NullableDataProperty?.IntField ?? throw new Exception());
Use(inst?.NullableDataField?.NullableDataProperty?.NullableIntField ?? throw new Exception());
Use(inst?.NullableDataProperty?.NullableDataProperty?.IntField ?? throw new Exception());
Use(inst?.NullableDataProperty?.NullableDataProperty?.NullableIntField ?? throw new Exception());
}
public static void Use<T>(T usage)
{
}
}
}

10
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ValueTypes.cs

@ -66,6 +66,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -66,6 +66,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
S s = this;
s.SetField();
}
public void UseField(int val)
{
UseField(Get<S>().Field);
}
}
#if CS72
@ -263,5 +268,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -263,5 +268,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
r.Property = 2;
#endif
}
public static void CallOnFieldOfTemporary()
{
Get<S>().Field.ToString();
}
}
}

13
ICSharpCode.Decompiler/CSharp/Annotations.cs

@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.Semantics;
@ -104,7 +105,17 @@ namespace ICSharpCode.Decompiler.CSharp @@ -104,7 +105,17 @@ namespace ICSharpCode.Decompiler.CSharp
public static ISymbol GetSymbol(this AstNode node)
{
var rr = node.Annotation<ResolveResult>();
return rr != null ? rr.GetSymbol() : null;
if (rr is MethodGroupResolveResult) {
// delegate construction?
var newObj = node.Annotation<NewObj>();
var funcptr = newObj?.Arguments.ElementAtOrDefault(1);
if (funcptr is LdFtn ldftn) {
return ldftn.Method;
} else if (funcptr is LdVirtFtn ldVirtFtn) {
return ldVirtFtn.Method;
}
}
return rr?.GetSymbol();
}
/// <summary>

26
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -134,7 +134,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -134,7 +134,7 @@ namespace ICSharpCode.Decompiler.CSharp
};
var cexpr = inst.AcceptVisitor(this, context);
#if DEBUG
if (inst.ResultType != StackType.Void && cexpr.Type.Kind != TypeKind.Unknown && inst.ResultType != StackType.Unknown) {
if (inst.ResultType != StackType.Void && cexpr.Type.Kind != TypeKind.Unknown && inst.ResultType != StackType.Unknown && cexpr.Type.Kind != TypeKind.None) {
// Validate the Translate post-condition (documented at beginning of this file):
if (inst.ResultType.IsIntegerType()) {
Debug.Assert(cexpr.Type.GetStackType().IsIntegerType(), "IL instructions of integer type must convert into C# expressions of integer type");
@ -561,7 +561,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -561,7 +561,7 @@ namespace ICSharpCode.Decompiler.CSharp
var argUType = NullableType.GetUnderlyingType(argument.Type);
if (argUType.GetStackType().GetSize() < inst.UnderlyingResultType.GetSize()
|| argUType.Kind == TypeKind.Enum && argUType.IsSmallIntegerType()
|| argUType.Kind == TypeKind.Enum && argUType.IsSmallIntegerType()
|| argUType.GetStackType() == StackType.I
|| argUType.IsKnownType(KnownTypeCode.Boolean)
|| argUType.IsKnownType(KnownTypeCode.Char))
@ -949,6 +949,13 @@ namespace ICSharpCode.Decompiler.CSharp @@ -949,6 +949,13 @@ namespace ICSharpCode.Decompiler.CSharp
.WithILInstruction(inst);
}
protected internal override TranslatedExpression VisitThrow(Throw inst, TranslationContext context)
{
return new ThrowExpression(Translate(inst.Argument))
.WithILInstruction(inst)
.WithRR(new ThrowResolveResult());
}
protected internal override TranslatedExpression VisitUserDefinedLogicOperator(UserDefinedLogicOperator inst, TranslationContext context)
{
var left = Translate(inst.Left, inst.Method.Parameters[0].Type).ConvertTo(inst.Method.Parameters[0].Type, this);
@ -1521,7 +1528,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1521,7 +1528,7 @@ namespace ICSharpCode.Decompiler.CSharp
var pao = GetPointerArithmeticOffset(inst.Value, value, ((PointerType)target.Type).ElementType, inst.CheckForOverflow);
if (pao != null) {
value = pao.Value;
} else {
} else {
value.Expression.AddChild(new Comment("ILSpy Error: GetPointerArithmeticOffset() failed", CommentType.MultiLine), Roles.Comment);
}
} else {
@ -1945,7 +1952,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1945,7 +1952,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (translatedTarget.Expression is DirectionExpression) {
// (ref x).member => x.member
translatedTarget = translatedTarget.UnwrapChild(((DirectionExpression)translatedTarget).Expression);
} else if (translatedTarget.Expression is UnaryOperatorExpression uoe
} else if (translatedTarget.Expression is UnaryOperatorExpression uoe
&& uoe.Operator == UnaryOperatorType.NullConditional
&& uoe.Expression is DirectionExpression) {
// (ref x)?.member => x?.member
@ -2138,7 +2145,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2138,7 +2145,7 @@ namespace ICSharpCode.Decompiler.CSharp
TranslatedExpression arrayExpr = Translate(inst.Array);
var arrayType = arrayExpr.Type as ArrayType;
if (arrayType == null || !TypeUtils.IsCompatibleTypeForMemoryAccess(arrayType.ElementType, inst.Type)) {
arrayType = new ArrayType(compilation, inst.Type, inst.Indices.Count);
arrayType = new ArrayType(compilation, inst.Type, inst.Indices.Count);
arrayExpr = arrayExpr.ConvertTo(arrayType, this);
}
TranslatedExpression expr = new IndexerExpression(
@ -2185,8 +2192,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2185,8 +2192,7 @@ namespace ICSharpCode.Decompiler.CSharp
// try via its effective base class.
arg = arg.ConvertTo(((ITypeParameter)targetType).EffectiveBaseClass, this);
}
}
else {
} else {
// Before unboxing arg must be a object
arg = arg.ConvertTo(compilation.FindType(KnownTypeCode.Object), this);
}
@ -2425,7 +2431,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2425,7 +2431,7 @@ namespace ICSharpCode.Decompiler.CSharp
}
TranslatedExpression MakeInitializerAssignment(InitializedObjectResolveResult rr, IL.Transforms.AccessPathElement memberPath,
IL.Transforms.AccessPathElement valuePath, List<TranslatedExpression> values,
IL.Transforms.AccessPathElement valuePath, List<TranslatedExpression> values,
Dictionary<ILVariable, ILInstruction> indexVariables)
{
TranslatedExpression value;
@ -2658,7 +2664,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2658,7 +2664,9 @@ namespace ICSharpCode.Decompiler.CSharp
var rr = resolver.ResolveBinaryOperator(BinaryOperatorType.NullCoalescing, value.ResolveResult, fallback.ResolveResult);
if (rr.IsError) {
IType targetType;
if (!value.Type.Equals(SpecialType.NullType) && !fallback.Type.Equals(SpecialType.NullType) && !value.Type.Equals(fallback.Type)) {
if (fallback.Expression is ThrowExpression && fallback.Type.Equals(SpecialType.NoType)) {
targetType = NullableType.GetUnderlyingType(value.Type);
} else if (!value.Type.Equals(SpecialType.NullType) && !fallback.Type.Equals(SpecialType.NullType) && !value.Type.Equals(fallback.Type)) {
targetType = compilation.FindType(inst.UnderlyingResultType.ToKnownTypeCode());
} else {
targetType = value.Type.Equals(SpecialType.NullType) ? fallback.Type : value.Type;

3
ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs

@ -115,6 +115,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -115,6 +115,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
if (c != Conversion.None)
return c;
}
if (resolveResult is ThrowResolveResult) {
return Conversion.ThrowExpressionConversion;
}
if (allowUserDefined && allowTuple) {
// if allowUserDefined and allowTuple are true, we might as well use the cache
c = ImplicitConversion(resolveResult.Type, toType);

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

@ -527,12 +527,18 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -527,12 +527,18 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
Attribute attr = new Attribute();
attr.Type = ConvertAttributeType(attribute.AttributeType);
SimpleType st = attr.Type as SimpleType;
MemberType mt = attr.Type as MemberType;
if (st != null && st.Identifier.EndsWith("Attribute", StringComparison.Ordinal)) {
st.Identifier = st.Identifier.Substring(0, st.Identifier.Length - 9);
} else if (mt != null && mt.MemberName.EndsWith("Attribute", StringComparison.Ordinal)) {
mt.MemberName = mt.MemberName.Substring(0, mt.MemberName.Length - 9);
switch (attr.Type) {
case SimpleType st:
if (st.Identifier.EndsWith("Attribute", StringComparison.Ordinal))
st.Identifier = st.Identifier.Substring(0, st.Identifier.Length - 9);
break;
case MemberType mt:
if (mt.MemberName.EndsWith("Attribute", StringComparison.Ordinal))
mt.MemberName = mt.MemberName.Substring(0, mt.MemberName.Length - 9);
break;
}
if (AddResolveResultAnnotations && attribute.Constructor != null) {
attr.AddAnnotation(new MemberResolveResult(null, attribute.Constructor));
}
var parameters = attribute.Constructor?.Parameters ?? EmptyList<IParameter>.Instance;
for (int i = 0; i < attribute.FixedArguments.Length; i++) {

16
ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs

@ -163,7 +163,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -163,7 +163,7 @@ namespace ICSharpCode.Decompiler.CSharp
}
throw new ArgumentException("descendant must be a descendant of the current node");
}
/// <summary>
/// Adds casts (if necessary) to convert this expression to the specified target type.
/// </summary>
@ -176,6 +176,17 @@ namespace ICSharpCode.Decompiler.CSharp @@ -176,6 +176,17 @@ namespace ICSharpCode.Decompiler.CSharp
///
/// From the caller's perspective, IntPtr/UIntPtr behave like normal C# integers except that they have native int size.
/// All the special cases necessary to make IntPtr/UIntPtr behave sanely are handled internally in ConvertTo().
///
/// Post-condition:
/// The "expected evaluation result" is the value computed by <c>this.Expression</c>,
/// converted to targetType via an IL conv instruction.
///
/// ConvertTo(targetType, allowImplicitConversion=false).Type must be equal to targetType (modulo identity conversions).
/// The value computed by the converted expression must match the "expected evaluation result".
///
/// ConvertTo(targetType, allowImplicitConversion=true) must produce an expression that,
/// when evaluated in a context where it will be implicitly converted to targetType,
/// evaluates to the "expected evaluation result".
/// </remarks>
public TranslatedExpression ConvertTo(IType targetType, ExpressionBuilder expressionBuilder, bool checkForOverflow = false, bool allowImplicitConversion = false)
{
@ -227,6 +238,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -227,6 +238,9 @@ namespace ICSharpCode.Decompiler.CSharp
.WithRR(convAnnotation.ConversionResolveResult)
.ConvertTo(targetType, expressionBuilder, checkForOverflow, allowImplicitConversion);
}
if (Expression is ThrowExpression && allowImplicitConversion) {
return this; // Throw expressions have no type and are implicitly convertible to any type
}
if (Expression is TupleExpression tupleExpr && targetType is TupleType targetTupleType
&& tupleExpr.Elements.Count == targetTupleType.ElementTypes.Length)
{

20
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -88,6 +88,7 @@ namespace ICSharpCode.Decompiler @@ -88,6 +88,7 @@ namespace ICSharpCode.Decompiler
}
if (languageVersion < CSharp.LanguageVersion.CSharp7) {
outVariables = false;
throwExpressions = false;
tupleTypes = false;
tupleConversions = false;
discards = false;
@ -118,7 +119,7 @@ namespace ICSharpCode.Decompiler @@ -118,7 +119,7 @@ namespace ICSharpCode.Decompiler
if (introduceRefModifiersOnStructs || introduceReadonlyAndInModifiers || nonTrailingNamedArguments || refExtensionMethods)
return CSharp.LanguageVersion.CSharp7_2;
// C# 7.1 missing
if (outVariables || tupleTypes || tupleConversions || discards || localFunctions)
if (outVariables || throwExpressions || tupleTypes || tupleConversions || discards || localFunctions)
return CSharp.LanguageVersion.CSharp7;
if (awaitInCatchFinally || useExpressionBodyForCalculatedGetterOnlyProperties || nullPropagation
|| stringInterpolation || dictionaryInitializers || extensionMethodsInCollectionInitializers)
@ -874,6 +875,23 @@ namespace ICSharpCode.Decompiler @@ -874,6 +875,23 @@ namespace ICSharpCode.Decompiler
}
}
bool throwExpressions = true;
/// <summary>
/// Gets/Sets whether throw expressions should be used.
/// </summary>
[Category("C# 7.0 / VS 2017")]
[Description("DecompilerSettings.UseThrowExpressions")]
public bool ThrowExpressions {
get { return throwExpressions; }
set {
if (throwExpressions != value) {
throwExpressions = value;
OnPropertyChanged();
}
}
}
bool tupleConversions = true;
/// <summary>

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -384,6 +384,7 @@ @@ -384,6 +384,7 @@
<Compile Include="TypeSystem\ModifiedType.cs" />
<Compile Include="TypeSystem\Implementation\PinnedType.cs" />
<Compile Include="Metadata\MetadataExtensions.cs" />
<Compile Include="Semantics\ThrowResolveResult.cs" />
<Compile Include="Semantics\TupleResolveResult.cs" />
<Compile Include="TypeSystem\NormalizeTypeVisitor.cs" />
<Compile Include="TypeSystem\Nullability.cs" />

2
ICSharpCode.Decompiler/IL/Instructions.cs

@ -4432,7 +4432,7 @@ namespace ICSharpCode.Decompiler.IL @@ -4432,7 +4432,7 @@ namespace ICSharpCode.Decompiler.IL
public Throw(ILInstruction argument) : base(OpCode.Throw, argument)
{
}
public override StackType ResultType { get { return StackType.Void; } }
public override StackType ResultType { get { return this.resultType; } }
protected override InstructionFlags ComputeFlags()
{
return base.ComputeFlags() | InstructionFlags.MayThrow | InstructionFlags.EndPointUnreachable;

2
ICSharpCode.Decompiler/IL/Instructions.tt

@ -268,7 +268,7 @@ @@ -268,7 +268,7 @@
new OpCode("default.value", "Returns the default value for a type.",
NoArguments, HasTypeOperand, ResultType("type.GetStackType()")),
new OpCode("throw", "Throws an exception.",
Unary, MayThrow, UnconditionalBranch),
Unary, MayThrow, HasFlag("InstructionFlags.EndPointUnreachable"), ResultType("this.resultType")),
new OpCode("rethrow", "Rethrows the current exception.",
NoArguments, MayThrow, UnconditionalBranch),
new OpCode("sizeof", "Gets the size of a type in bytes.",

2
ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs

@ -308,7 +308,7 @@ namespace ICSharpCode.Decompiler.IL @@ -308,7 +308,7 @@ namespace ICSharpCode.Decompiler.IL
protected abstract SlotInfo GetChildSlot(int index);
#region ChildrenCollection + ChildrenEnumerator
public struct ChildrenCollection : IReadOnlyList<ILInstruction>
public readonly struct ChildrenCollection : IReadOnlyList<ILInstruction>
{
readonly ILInstruction inst;

5
ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs

@ -362,4 +362,9 @@ namespace ICSharpCode.Decompiler.IL @@ -362,4 +362,9 @@ namespace ICSharpCode.Decompiler.IL
}
}
}
public partial class Throw
{
internal StackType resultType = StackType.Void;
}
}

11
ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs

@ -105,13 +105,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -105,13 +105,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// Parameters can be copied only if they aren't assigned to (directly or indirectly via ldarga)
// note: the initialization by the caller is the first store -> StoreCount must be 1
return v.IsSingleDefinition;
case VariableKind.StackSlot:
case VariableKind.ExceptionStackSlot:
// Variables are be copied only if both they and the target copy variable are generated,
// and if the variable has only a single assignment
return v.IsSingleDefinition && target.Kind == VariableKind.StackSlot;
default:
return false;
// Variables can be copied if both are single-definition.
// To avoid removing too many variables, we do this only if the target
// is either a stackslot or a ref local.
Debug.Assert(target.IsSingleDefinition);
return v.IsSingleDefinition && (target.Kind == VariableKind.StackSlot || target.StackType == StackType.Ref);
}
default:
// All instructions without special behavior that target a stack-variable can be copied.

16
ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

@ -272,10 +272,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -272,10 +272,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
if (ldloca.Variable.Type.IsReferenceType ?? false)
return false;
switch (ldloca.Parent.OpCode) {
ILInstruction inst = ldloca;
while (inst.Parent is LdFlda ldflda) {
inst = ldflda;
}
switch (inst.Parent.OpCode) {
case OpCode.Call:
case OpCode.CallVirt:
var method = ((CallInstruction)ldloca.Parent).Method;
var method = ((CallInstruction)inst.Parent).Method;
if (method.IsAccessor && method.AccessorKind != MethodSemanticsAttributes.Getter) {
// C# doesn't allow calling setters on temporary structs
return false;
@ -284,7 +288,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -284,7 +288,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
case OpCode.Await:
return true;
case OpCode.NullableUnwrap:
return ((NullableUnwrap)ldloca.Parent).RefInput;
return ((NullableUnwrap)inst.Parent).RefInput;
default:
return false;
}
@ -346,13 +350,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -346,13 +350,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
switch (addr) {
case LdFlda ldflda:
return ldflda.Field.IsReadOnly;
return ldflda.Field.IsReadOnly
|| (ldflda.Field.DeclaringType.Kind == TypeKind.Struct && IsReadonlyReference(ldflda.Target));
case LdsFlda ldsflda:
return ldsflda.Field.IsReadOnly;
case LdLoc ldloc:
return IsReadonlyRefLocal(ldloc.Variable);
case Call call:
return call.Method.ReturnTypeIsRefReadOnly;
case AddressOf _:
// C# doesn't allow mutation of value-type temporaries
return true;
default:
return false;
}

82
ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs

@ -21,6 +21,7 @@ using System.Collections.Generic; @@ -21,6 +21,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms
{
@ -34,18 +35,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -34,18 +35,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
public void Run(Block block, int pos, StatementTransformContext context)
{
TransformRefTypes(block, pos, context);
if (!TransformRefTypes(block, pos, context)) {
TransformThrowExpressionValueTypes(block, pos, context);
}
}
/// <summary>
/// Handles NullCoalescingInstruction case 1: reference types.
///
/// stloc s(valueInst)
/// if (comp(ldloc s == ldnull)) {
/// stloc s(fallbackInst)
/// }
/// =>
/// stloc s(if.notnull(valueInst, fallbackInst))
/// </summary>
bool TransformRefTypes(Block block, int pos, StatementTransformContext context)
{
@ -58,6 +54,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -58,6 +54,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!(condition.MatchCompEquals(out var left, out var right) && left.MatchLdLoc(stloc.Variable) && right.MatchLdNull()))
return false;
trueInst = Block.Unwrap(trueInst);
// stloc s(valueInst)
// if (comp(ldloc s == ldnull)) {
// stloc s(fallbackInst)
// }
// =>
// stloc s(if.notnull(valueInst, fallbackInst))
if (trueInst.MatchStLoc(stloc.Variable, out var fallbackValue)) {
context.Step("NullCoalescingTransform: simple (reference types)", stloc);
stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, fallbackValue);
@ -83,7 +85,71 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -83,7 +85,71 @@ namespace ICSharpCode.Decompiler.IL.Transforms
ILInlining.InlineOneIfPossible(block, pos, InliningOptions.None, context);
return true;
}
// stloc obj(valueInst)
// if (comp(ldloc obj == ldnull)) {
// throw(...)
// }
// =>
// stloc obj(if.notnull(valueInst, throw(...)))
if (context.Settings.ThrowExpressions && trueInst is Throw throwInst) {
context.Step("NullCoalescingTransform (reference types + throw expression)", stloc);
throwInst.resultType = StackType.O;
stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, throwInst);
block.Instructions.RemoveAt(pos + 1); // remove if instruction
ILInlining.InlineOneIfPossible(block, pos, InliningOptions.None, context);
return true;
}
return false;
}
/// <summary>
/// stloc v(value)
/// if (logic.not(call get_HasValue(ldloca v))) throw(...)
/// ... Call(arg1, arg2, call GetValueOrDefault(ldloca v), arg4) ...
/// =>
/// ... Call(arg1, arg2, if.notnull(value, throw(...)), arg4) ...
/// </summary>
bool TransformThrowExpressionValueTypes(Block block, int pos, StatementTransformContext context)
{
if (pos + 2 >= block.Instructions.Count)
return false;
if (!(block.Instructions[pos] is StLoc stloc))
return false;
ILVariable v = stloc.Variable;
if (!(v.StoreCount == 1 && v.LoadCount == 0 && v.AddressCount == 2))
return false;
if (!block.Instructions[pos + 1].MatchIfInstruction(out var condition, out var trueInst))
return false;
if (!(Block.Unwrap(trueInst) is Throw throwInst))
return false;
if (!condition.MatchLogicNot(out var arg))
return false;
if (!(arg is CallInstruction call && NullableLiftingTransform.MatchHasValueCall(call, v)))
return false;
var throwInstParent = throwInst.Parent;
var throwInstChildIndex = throwInst.ChildIndex;
var nullCoalescingWithThrow = new NullCoalescingInstruction(
NullCoalescingKind.NullableWithValueFallback,
stloc.Value,
throwInst);
var resultType = NullableType.GetUnderlyingType(call.Method.DeclaringType).GetStackType();
nullCoalescingWithThrow.UnderlyingResultType = resultType;
var result = ILInlining.FindLoadInNext(block.Instructions[pos + 2], v, nullCoalescingWithThrow, InliningOptions.None);
if (result.Type == ILInlining.FindResultType.Found
&& NullableLiftingTransform.MatchGetValueOrDefault(result.LoadInst.Parent, v))
{
context.Step("NullCoalescingTransform (value types + throw expression)", stloc);
throwInst.resultType = resultType;
result.LoadInst.Parent.ReplaceWith(nullCoalescingWithThrow);
block.Instructions.RemoveRange(pos, 2); // remove store(s) and if instruction
return true;
} else {
// reset the primary position (see remarks on ILInstruction.Parent)
stloc.Value = stloc.Value;
var children = throwInstParent.Children;
children[throwInstChildIndex] = throwInst;
return false;
}
}
}
}

6
ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs

@ -186,6 +186,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -186,6 +186,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return chainLength >= 1;
} else if (inst.MatchLdFld(out var target, out _)) {
inst = target;
} else if (inst.MatchLdFlda(out target, out var f)) {
if (target is AddressOf addressOf && f.DeclaringType.Kind == TypeKind.Struct) {
inst = addressOf.Value;
} else {
inst = target;
}
} else if (inst is CallInstruction call && call.OpCode != OpCode.NewObj) {
if (call.Arguments.Count == 0) {
return false;

2
ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs

@ -833,7 +833,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -833,7 +833,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary>
/// Matches 'logic.not(call get_HasValue(ldloca v))'
/// </summary>
static bool MatchNegatedHasValueCall(ILInstruction inst, ILVariable v)
internal static bool MatchNegatedHasValueCall(ILInstruction inst, ILVariable v)
{
return inst.MatchLogicNot(out var arg) && MatchHasValueCall(arg, v);
}

12
ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs

@ -109,7 +109,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -109,7 +109,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// Address stored in local variable: also check all uses of that variable.
if (!(stloc.Variable.Kind == VariableKind.StackSlot || stloc.Variable.Kind == VariableKind.Local))
return AddressUse.Unknown;
if (stloc.Value.OpCode != OpCode.LdLoca) {
var value = stloc.Value;
while (value is LdFlda ldFlda) {
value = ldFlda.Target;
}
if (value.OpCode != OpCode.LdLoca) {
// GroupStores.HandleLoad() only detects ref-locals when they are directly initialized with ldloca
return AddressUse.Unknown;
}
@ -162,7 +166,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -162,7 +166,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return null; // only single-definition variables can be supported ref locals
var store = ldloc.Variable.StoreInstructions.SingleOrDefault();
if (store is StLoc stloc) {
return stloc.Value as LdLoca;
var value = stloc.Value;
while (value is LdFlda ldFlda) {
value = ldFlda.Target;
}
return value as LdLoca;
}
return null;
}

22
ICSharpCode.Decompiler/Semantics/Conversion.cs

@ -76,8 +76,16 @@ namespace ICSharpCode.Decompiler.Semantics @@ -76,8 +76,16 @@ namespace ICSharpCode.Decompiler.Semantics
/// </summary>
public static readonly Conversion TryCast = new BuiltinConversion(false, 9);
/// <summary>
/// C# 6 string interpolation expression implicitly being converted to <see cref="System.IFormattable"/> or <see cref="System.FormattableString"/>.
/// </summary>
public static readonly Conversion ImplicitInterpolatedStringConversion = new BuiltinConversion(true, 10);
/// <summary>
/// C# 7 throw expression being converted to an arbitrary type.
/// </summary>
public static readonly Conversion ThrowExpressionConversion = new BuiltinConversion(true, 11);
public static Conversion UserDefinedConversion(IMethod operatorMethod, bool isImplicit, Conversion conversionBeforeUserDefinedOperator, Conversion conversionAfterUserDefinedOperator, bool isLifted = false, bool isAmbiguous = false)
{
if (operatorMethod == null)
@ -231,7 +239,11 @@ namespace ICSharpCode.Decompiler.Semantics @@ -231,7 +239,11 @@ namespace ICSharpCode.Decompiler.Semantics
}
public override bool IsInterpolatedStringConversion => type == 10;
public override bool IsThrowExpressionConversion {
get { return type == 11; }
}
public override string ToString()
{
string name = null;
@ -263,6 +275,8 @@ namespace ICSharpCode.Decompiler.Semantics @@ -263,6 +275,8 @@ namespace ICSharpCode.Decompiler.Semantics
return "try cast";
case 10:
return "interpolated string";
case 11:
return "throw-expression conversion";
}
return (isImplicit ? "implicit " : "explicit ") + name + " conversion";
}
@ -449,7 +463,11 @@ namespace ICSharpCode.Decompiler.Semantics @@ -449,7 +463,11 @@ namespace ICSharpCode.Decompiler.Semantics
public virtual bool IsTryCast {
get { return false; }
}
public virtual bool IsThrowExpressionConversion {
get { return false; }
}
public virtual bool IsIdentityConversion {
get { return false; }
}

29
ICSharpCode.Decompiler/Semantics/ThrowResolveResult.cs

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
// Copyright (c) 2018 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 ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.Semantics
{
class ThrowResolveResult : ResolveResult
{
public ThrowResolveResult() : base(SpecialType.NoType)
{
}
}
}

18
ILSpy.BamlDecompiler/Rewrite/MarkupExtensionRewritePass.cs

@ -77,14 +77,16 @@ namespace ILSpy.BamlDecompiler.Rewrite { @@ -77,14 +77,16 @@ namespace ILSpy.BamlDecompiler.Rewrite {
var attrName = elem.Name;
if (attrName != key)
attrName = property.ToXName(ctx, parent, property.IsAttachedTo(type));
var attr = new XAttribute(attrName, extValue);
var list = new List<XAttribute>(parent.Attributes());
if (attrName == key)
list.Insert(0, attr);
else
list.Add(attr);
parent.RemoveAttributes();
parent.ReplaceAttributes(list);
if (!parent.Attributes(attrName).Any()) {
var attr = new XAttribute(attrName, extValue);
var list = new List<XAttribute>(parent.Attributes());
if (attrName == key)
list.Insert(0, attr);
else
list.Add(attr);
parent.RemoveAttributes();
parent.ReplaceAttributes(list);
}
elem.Remove();
return true;

9
ILSpy/Properties/Resources.Designer.cs generated

@ -935,6 +935,15 @@ namespace ICSharpCode.ILSpy.Properties { @@ -935,6 +935,15 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Use throw expressions.
/// </summary>
public static string DecompilerSettings_UseThrowExpressions {
get {
return ResourceManager.GetString("DecompilerSettings.UseThrowExpressions", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Use tuple type syntax.
/// </summary>

3
ILSpy/Properties/Resources.resx

@ -729,6 +729,9 @@ @@ -729,6 +729,9 @@
<data name="CannotAnalyzeMissingRef" xml:space="preserve">
<value>Entity could not be resolved. Cannot analyze entities from missing assembly references. Add the missing reference and try again.</value>
</data>
<data name="DecompilerSettings.UseThrowExpressions" xml:space="preserve">
<value>Use throw expressions</value>
</data>
<data name="DecompilerSettings.AllowExtensionMethodSyntaxOnRef" xml:space="preserve">
<value>Use 'ref' extension methods</value>
</data>

Loading…
Cancel
Save