mirror of https://github.com/icsharpcode/ILSpy.git
47 changed files with 54012 additions and 210 deletions
@ -0,0 +1,378 @@
@@ -0,0 +1,378 @@
|
||||
using System; |
||||
|
||||
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty |
||||
{ |
||||
internal static class Extension |
||||
{ |
||||
public static dynamic ToDynamic(this int i, dynamic info) |
||||
{ |
||||
throw null; |
||||
} |
||||
} |
||||
|
||||
internal class DynamicTests |
||||
{ |
||||
|
||||
private class Base |
||||
{ |
||||
public Base(object baseObj) |
||||
{ |
||||
} |
||||
} |
||||
|
||||
private class Derived : Base |
||||
{ |
||||
public Derived(dynamic d) |
||||
: base((object)d) |
||||
{ |
||||
} |
||||
} |
||||
|
||||
private struct MyValueType |
||||
{ |
||||
private readonly dynamic _getOnlyProperty; |
||||
public dynamic Field; |
||||
#if CS60
|
||||
public dynamic GetOnlyProperty => _getOnlyProperty; |
||||
#else
|
||||
public dynamic GetOnlyProperty { |
||||
get { |
||||
return _getOnlyProperty; |
||||
} |
||||
} |
||||
#endif
|
||||
|
||||
public dynamic Property { |
||||
get; |
||||
set; |
||||
} |
||||
|
||||
public void Method(dynamic a) |
||||
{ |
||||
|
||||
} |
||||
} |
||||
|
||||
private static dynamic field; |
||||
private static object objectField; |
||||
public dynamic Property { |
||||
get; |
||||
set; |
||||
} |
||||
|
||||
public DynamicTests() |
||||
{ |
||||
} |
||||
|
||||
public DynamicTests(dynamic test) |
||||
{ |
||||
} |
||||
|
||||
public DynamicTests(DynamicTests test) |
||||
{ |
||||
} |
||||
|
||||
private static void InvokeConstructor() |
||||
{ |
||||
DynamicTests dynamicTests = new DynamicTests(); |
||||
dynamic val = new DynamicTests(); |
||||
val.Test(new UnauthorizedAccessException()); |
||||
dynamic val2 = new DynamicTests(val); |
||||
val2.Get(new DynamicTests((DynamicTests)val)); |
||||
val2.Call(new DynamicTests((dynamic)dynamicTests)); |
||||
} |
||||
|
||||
private static dynamic InlineAssign(object a, out dynamic b) |
||||
{ |
||||
return b = ((dynamic)a).Test; |
||||
} |
||||
|
||||
private static dynamic SelfReference(dynamic d) |
||||
{ |
||||
return d[d, d] = d; |
||||
} |
||||
|
||||
private static dynamic LongArgumentListFunc(dynamic d) |
||||
{ |
||||
// Func`13
|
||||
return d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); |
||||
} |
||||
|
||||
private static void LongArgumentListAction(dynamic d) |
||||
{ |
||||
// Action`13
|
||||
d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11); |
||||
} |
||||
|
||||
private static void DynamicThrow() |
||||
{ |
||||
try { |
||||
throw (Exception)field; |
||||
} catch (Exception ex) { |
||||
Console.WriteLine(ex.ToString()); |
||||
throw; |
||||
} |
||||
} |
||||
|
||||
private static void MemberAccess(dynamic a) |
||||
{ |
||||
a.Test1(); |
||||
a.GenericTest<int, int>(); |
||||
a.Test2(1); |
||||
a.Test3(a.InnerTest(1, 2, 3, 4, 5)); |
||||
a.Test4(2, null, a.Index[0]); |
||||
a.Test5(a, a.Number, a.String); |
||||
a[0] = 3; |
||||
a.Index[a.Number] = 5; |
||||
a.Index[a.Number] += 5; |
||||
a.Setter = new DynamicTests(); |
||||
a.Setter2 = 5; |
||||
} |
||||
|
||||
private static void StructMemberAccess(MyValueType valueType) |
||||
{ |
||||
valueType.Field = 0; |
||||
valueType.Field += 5; |
||||
valueType.Field[1] = 5; |
||||
valueType.Field.CallMe(); |
||||
DynamicTests.Casts(valueType.GetOnlyProperty); |
||||
valueType.GetOnlyProperty.CallMe(); |
||||
valueType.Property = 0; |
||||
valueType.Property += 5; |
||||
valueType.Property[1] = 5; |
||||
valueType.Property.CallMe(5.ToDynamic((object)valueType.Property.Call())); |
||||
valueType.Method(valueType.GetOnlyProperty + valueType.Field); |
||||
} |
||||
|
||||
private static void RequiredCasts() |
||||
{ |
||||
((dynamic)objectField).A = 5; |
||||
((dynamic)objectField).B += 5; |
||||
((dynamic)objectField).Call(); |
||||
((object)field).ToString(); |
||||
field.Call("Hello World"); |
||||
field.Call((object)"Hello World"); |
||||
field.Call((dynamic)"Hello World"); |
||||
} |
||||
|
||||
private static void DynamicCallWithString() |
||||
{ |
||||
field.Call("Hello World"); |
||||
} |
||||
|
||||
private static void DynamicCallWithNamedArgs() |
||||
{ |
||||
field.Call(a: "Hello World"); |
||||
} |
||||
|
||||
private static void DynamicCallWithRefOutArg(int a, out int b) |
||||
{ |
||||
field.Call(ref a, out b); |
||||
} |
||||
|
||||
private static void DynamicCallWithStringCastToObj() |
||||
{ |
||||
field.Call((object)"Hello World"); |
||||
} |
||||
|
||||
private static void DynamicCallWithStringCastToDynamic() |
||||
{ |
||||
field.Call((dynamic)"Hello World"); |
||||
} |
||||
|
||||
private static void DynamicCallWithStringCastToDynamic2() |
||||
{ |
||||
field.Call((dynamic)"Hello World", 5, null); |
||||
} |
||||
|
||||
private static void DynamicCallWithStringCastToDynamic3() |
||||
{ |
||||
field.Call((dynamic)"Hello World", 5u, null); |
||||
} |
||||
|
||||
private static void Invocation(dynamic a, dynamic b) |
||||
{ |
||||
a(null, b.Test()); |
||||
} |
||||
|
||||
private static dynamic Test1(dynamic a) |
||||
{ |
||||
dynamic val = a.IndexedProperty; |
||||
return val[0]; |
||||
} |
||||
|
||||
private static dynamic Test2(dynamic a) |
||||
{ |
||||
return a.IndexedProperty[0]; |
||||
} |
||||
|
||||
private static void ArithmeticBinaryOperators(dynamic a, dynamic b) |
||||
{ |
||||
DynamicTests.MemberAccess(a + b); |
||||
DynamicTests.MemberAccess(a + 1); |
||||
DynamicTests.MemberAccess(a + null); |
||||
DynamicTests.MemberAccess(a - b); |
||||
DynamicTests.MemberAccess(a - 1); |
||||
DynamicTests.MemberAccess(a - null); |
||||
DynamicTests.MemberAccess(a * b); |
||||
DynamicTests.MemberAccess(a * 1); |
||||
DynamicTests.MemberAccess(a * null); |
||||
DynamicTests.MemberAccess(a / b); |
||||
DynamicTests.MemberAccess(a / 1); |
||||
DynamicTests.MemberAccess(a / null); |
||||
DynamicTests.MemberAccess(a % b); |
||||
DynamicTests.MemberAccess(a % 1); |
||||
DynamicTests.MemberAccess(a % null); |
||||
} |
||||
|
||||
private static void CheckedArithmeticBinaryOperators(dynamic a, dynamic b) |
||||
{ |
||||
checked { |
||||
DynamicTests.MemberAccess(a + b); |
||||
DynamicTests.MemberAccess(a + 1); |
||||
DynamicTests.MemberAccess(a + null); |
||||
DynamicTests.MemberAccess(a - b); |
||||
DynamicTests.MemberAccess(a - 1); |
||||
DynamicTests.MemberAccess(a - null); |
||||
DynamicTests.MemberAccess(a * b); |
||||
DynamicTests.MemberAccess(a * 1); |
||||
DynamicTests.MemberAccess(a * null); |
||||
DynamicTests.MemberAccess(a / b); |
||||
DynamicTests.MemberAccess(a / 1); |
||||
DynamicTests.MemberAccess(a / null); |
||||
DynamicTests.MemberAccess(a % b); |
||||
DynamicTests.MemberAccess(a % 1); |
||||
DynamicTests.MemberAccess(a % null); |
||||
} |
||||
} |
||||
|
||||
private static void UncheckedArithmeticBinaryOperators(dynamic a, dynamic b) |
||||
{ |
||||
checked { |
||||
DynamicTests.MemberAccess(a + b); |
||||
DynamicTests.MemberAccess(a + 1); |
||||
DynamicTests.MemberAccess(a + null); |
||||
DynamicTests.MemberAccess(unchecked(a - b)); |
||||
DynamicTests.MemberAccess(a - 1); |
||||
DynamicTests.MemberAccess(a - null); |
||||
DynamicTests.MemberAccess(unchecked(a * b)); |
||||
DynamicTests.MemberAccess(a * 1); |
||||
DynamicTests.MemberAccess(a * null); |
||||
DynamicTests.MemberAccess(a / b); |
||||
DynamicTests.MemberAccess(a / 1); |
||||
DynamicTests.MemberAccess(a / null); |
||||
DynamicTests.MemberAccess(a % b); |
||||
DynamicTests.MemberAccess(a % 1); |
||||
DynamicTests.MemberAccess(a % null); |
||||
} |
||||
} |
||||
|
||||
private static void RelationalOperators(dynamic a, dynamic b) |
||||
{ |
||||
DynamicTests.MemberAccess(a == b); |
||||
DynamicTests.MemberAccess(a == 1); |
||||
DynamicTests.MemberAccess(a == null); |
||||
DynamicTests.MemberAccess(a != b); |
||||
DynamicTests.MemberAccess(a != 1); |
||||
DynamicTests.MemberAccess(a != null); |
||||
DynamicTests.MemberAccess(a < b); |
||||
DynamicTests.MemberAccess(a < 1); |
||||
DynamicTests.MemberAccess(a < null); |
||||
DynamicTests.MemberAccess(a > b); |
||||
DynamicTests.MemberAccess(a > 1); |
||||
DynamicTests.MemberAccess(a > null); |
||||
DynamicTests.MemberAccess(a >= b); |
||||
DynamicTests.MemberAccess(a >= 1); |
||||
DynamicTests.MemberAccess(a >= null); |
||||
DynamicTests.MemberAccess(a <= b); |
||||
DynamicTests.MemberAccess(a <= 1); |
||||
DynamicTests.MemberAccess(a <= null); |
||||
} |
||||
|
||||
private static void Casts(dynamic a) |
||||
{ |
||||
Console.WriteLine(); |
||||
MemberAccess((int)a); |
||||
MemberAccess(checked((int)a)); |
||||
} |
||||
|
||||
private static void M(object o) |
||||
{ |
||||
} |
||||
|
||||
private static void M2(dynamic d) |
||||
{ |
||||
} |
||||
|
||||
private static void M3(int i) |
||||
{ |
||||
} |
||||
|
||||
private static void NotDynamicDispatch(dynamic d) |
||||
{ |
||||
DynamicTests.M(d); |
||||
M((object)d); |
||||
DynamicTests.M2(d); |
||||
M2((object)d); |
||||
} |
||||
|
||||
private static void CompoundAssignment(dynamic a, dynamic b) |
||||
{ |
||||
a.Setter2 += 5; |
||||
a.Setter2 -= 1; |
||||
a.Setter2 *= 2; |
||||
a.Setter2 /= 5; |
||||
a.Setter2 += b; |
||||
a.Setter2 -= b; |
||||
a.Setter2 *= b; |
||||
a.Setter2 /= b; |
||||
field.Setter += 5; |
||||
field.Setter -= 5; |
||||
} |
||||
|
||||
private static void InlineCompoundAssignment(dynamic a, dynamic b) |
||||
{ |
||||
Console.WriteLine(a.Setter2 += 5); |
||||
Console.WriteLine(a.Setter2 -= 1); |
||||
Console.WriteLine(a.Setter2 *= 2); |
||||
Console.WriteLine(a.Setter2 /= 5); |
||||
Console.WriteLine(a.Setter2 += b); |
||||
Console.WriteLine(a.Setter2 -= b); |
||||
Console.WriteLine(a.Setter2 *= b); |
||||
Console.WriteLine(a.Setter2 /= b); |
||||
} |
||||
|
||||
private static void UnaryOperators(dynamic a) |
||||
{ |
||||
// TODO : beautify inc/dec on locals and fields
|
||||
//a--;
|
||||
//a++;
|
||||
//--a;
|
||||
//++a;
|
||||
DynamicTests.Casts(-a); |
||||
DynamicTests.Casts(+a); |
||||
} |
||||
|
||||
private static void Loops(dynamic list) |
||||
{ |
||||
foreach (dynamic item in list) { |
||||
DynamicTests.UnaryOperators(item); |
||||
} |
||||
} |
||||
|
||||
private static void If(dynamic a, dynamic b) |
||||
{ |
||||
if (a == b) { |
||||
Console.WriteLine("Equal"); |
||||
} |
||||
} |
||||
|
||||
private static void If2(dynamic a, dynamic b) |
||||
{ |
||||
// TODO : beautify complex conditions
|
||||
//if (a == null || b == null) {
|
||||
// Console.WriteLine("Equal");
|
||||
//}
|
||||
} |
||||
} |
||||
} |
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,529 @@
@@ -0,0 +1,529 @@
|
||||
// Copyright (c) 2018 Siegfried Pammer
|
||||
//
|
||||
// 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.Linq.Expressions; |
||||
using ICSharpCode.Decompiler.IL.Patterns; |
||||
using ICSharpCode.Decompiler.TypeSystem; |
||||
using ICSharpCode.Decompiler.Util; |
||||
|
||||
namespace ICSharpCode.Decompiler.IL |
||||
{ |
||||
[Flags] |
||||
public enum CSharpArgumentInfoFlags |
||||
{ |
||||
None = 0, |
||||
UseCompileTimeType = 1, |
||||
Constant = 2, |
||||
NamedArgument = 4, |
||||
IsRef = 8, |
||||
IsOut = 0x10, |
||||
IsStaticType = 0x20 |
||||
} |
||||
|
||||
[Flags] |
||||
public enum CSharpBinderFlags |
||||
{ |
||||
None = 0, |
||||
CheckedContext = 1, |
||||
InvokeSimpleName = 2, |
||||
InvokeSpecialName = 4, |
||||
BinaryOperationLogical = 8, |
||||
ConvertExplicit = 0x10, |
||||
ConvertArrayIndex = 0x20, |
||||
ResultIndexed = 0x40, |
||||
ValueFromCompoundAssignment = 0x80, |
||||
ResultDiscarded = 0x100 |
||||
} |
||||
|
||||
public struct CSharpArgumentInfo |
||||
{ |
||||
public string Name { get; set; } |
||||
public CSharpArgumentInfoFlags Flags { get; set; } |
||||
public IType CompileTimeType { get; set; } |
||||
|
||||
public bool HasFlag(CSharpArgumentInfoFlags flag) => (Flags & flag) != 0; |
||||
} |
||||
|
||||
partial class DynamicInstruction |
||||
{ |
||||
public CSharpBinderFlags BinderFlags { get; } |
||||
public IType CallingContext { get; } |
||||
|
||||
protected DynamicInstruction(OpCode opCode, CSharpBinderFlags binderFlags, IType context) |
||||
: base(opCode) |
||||
{ |
||||
BinderFlags = binderFlags; |
||||
CallingContext = context; |
||||
} |
||||
|
||||
protected void WriteBinderFlags(ITextOutput output, ILAstWritingOptions options) |
||||
{ |
||||
WriteBinderFlags(BinderFlags, output, options); |
||||
} |
||||
|
||||
internal static void WriteBinderFlags(CSharpBinderFlags flags, ITextOutput output, ILAstWritingOptions options) |
||||
{ |
||||
if ((flags & CSharpBinderFlags.BinaryOperationLogical) != 0) |
||||
output.Write(".logic"); |
||||
if ((flags & CSharpBinderFlags.CheckedContext) != 0) |
||||
output.Write(".checked"); |
||||
if ((flags & CSharpBinderFlags.ConvertArrayIndex) != 0) |
||||
output.Write(".arrayindex"); |
||||
if ((flags & CSharpBinderFlags.ConvertExplicit) != 0) |
||||
output.Write(".explicit"); |
||||
if ((flags & CSharpBinderFlags.InvokeSimpleName) != 0) |
||||
output.Write(".invokesimple"); |
||||
if ((flags & CSharpBinderFlags.InvokeSpecialName) != 0) |
||||
output.Write(".invokespecial"); |
||||
if ((flags & CSharpBinderFlags.ResultDiscarded) != 0) |
||||
output.Write(".discard"); |
||||
if ((flags & CSharpBinderFlags.ResultIndexed) != 0) |
||||
output.Write(".resultindexed"); |
||||
if ((flags & CSharpBinderFlags.ValueFromCompoundAssignment) != 0) |
||||
output.Write(".compound"); |
||||
} |
||||
|
||||
public abstract CSharpArgumentInfo GetArgumentInfoOfChild(int index); |
||||
|
||||
internal static void WriteArgumentList(ITextOutput output, ILAstWritingOptions options, params (ILInstruction, CSharpArgumentInfo)[] arguments) |
||||
{ |
||||
WriteArgumentList(output, options, (IEnumerable<(ILInstruction, CSharpArgumentInfo)>)arguments); |
||||
} |
||||
|
||||
internal static void WriteArgumentList(ITextOutput output, ILAstWritingOptions options, IEnumerable<(ILInstruction, CSharpArgumentInfo)> arguments) |
||||
{ |
||||
output.Write('('); |
||||
int j = 0; |
||||
foreach (var (arg, info) in arguments) { |
||||
if (j > 0) |
||||
output.Write(", "); |
||||
output.Write("[flags: "); |
||||
output.Write(info.Flags.ToString()); |
||||
output.Write(", name: " + info.Name + "] "); |
||||
arg.WriteTo(output, options); |
||||
j++; |
||||
} |
||||
output.Write(')'); |
||||
} |
||||
} |
||||
|
||||
partial class DynamicConvertInstruction |
||||
{ |
||||
public override void WriteTo(ITextOutput output, ILAstWritingOptions options) |
||||
{ |
||||
ILRange.WriteTo(output, options); |
||||
output.Write(OpCode); |
||||
WriteBinderFlags(output, options); |
||||
output.Write(' '); |
||||
type.WriteTo(output); |
||||
output.Write('('); |
||||
argument.WriteTo(output, options); |
||||
output.Write(')'); |
||||
} |
||||
|
||||
public DynamicConvertInstruction(CSharpBinderFlags binderFlags, IType type, IType context, ILInstruction argument) |
||||
: base(OpCode.DynamicConvertInstruction, binderFlags, context) |
||||
{ |
||||
Type = type; |
||||
Argument = argument; |
||||
} |
||||
|
||||
protected internal override bool PerformMatch(ref ListMatch listMatch, ref Match match) |
||||
{ |
||||
return base.PerformMatch(ref listMatch, ref match); |
||||
} |
||||
|
||||
public override StackType ResultType => type.GetStackType(); |
||||
|
||||
public bool IsChecked => (BinderFlags & CSharpBinderFlags.CheckedContext) != 0; |
||||
|
||||
public bool IsExplicit => (BinderFlags & CSharpBinderFlags.ConvertExplicit) != 0; |
||||
|
||||
public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) |
||||
{ |
||||
return default(CSharpArgumentInfo); |
||||
} |
||||
} |
||||
|
||||
partial class DynamicInvokeMemberInstruction |
||||
{ |
||||
public string Name { get; } |
||||
public IReadOnlyList<IType> TypeArguments { get; } |
||||
public IReadOnlyList<CSharpArgumentInfo> ArgumentInfo { get; } |
||||
|
||||
public DynamicInvokeMemberInstruction(CSharpBinderFlags binderFlags, string name, IType[] typeArguments, IType context, CSharpArgumentInfo[] argumentInfo, ILInstruction[] arguments) |
||||
: base(OpCode.DynamicInvokeMemberInstruction, binderFlags, context) |
||||
{ |
||||
Name = name; |
||||
TypeArguments = typeArguments ?? Empty<IType>.Array; |
||||
ArgumentInfo = argumentInfo; |
||||
Arguments = new InstructionCollection<ILInstruction>(this, 0); |
||||
Arguments.AddRange(arguments); |
||||
} |
||||
|
||||
public override void WriteTo(ITextOutput output, ILAstWritingOptions options) |
||||
{ |
||||
ILRange.WriteTo(output, options); |
||||
output.Write(OpCode); |
||||
WriteBinderFlags(output, options); |
||||
output.Write(' '); |
||||
output.Write(Name); |
||||
if (TypeArguments.Count > 0) { |
||||
output.Write('<'); |
||||
int i = 0; |
||||
foreach (var typeArg in TypeArguments) { |
||||
if (i > 0) |
||||
output.Write(", "); |
||||
typeArg.WriteTo(output); |
||||
i++; |
||||
} |
||||
output.Write('>'); |
||||
} |
||||
WriteArgumentList(output, options, Arguments.Zip(ArgumentInfo)); |
||||
} |
||||
|
||||
public override StackType ResultType => StackType.O; |
||||
|
||||
public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) |
||||
{ |
||||
if (index < 0 || index >= ArgumentInfo.Count) |
||||
throw new ArgumentOutOfRangeException(nameof(index)); |
||||
return ArgumentInfo[index]; |
||||
} |
||||
} |
||||
|
||||
partial class DynamicGetMemberInstruction |
||||
{ |
||||
public string Name { get; } |
||||
public CSharpArgumentInfo TargetArgumentInfo { get; } |
||||
|
||||
public DynamicGetMemberInstruction(CSharpBinderFlags binderFlags, string name, IType context, CSharpArgumentInfo targetArgumentInfo, ILInstruction target) |
||||
: base(OpCode.DynamicGetMemberInstruction, binderFlags, context) |
||||
{ |
||||
Name = name; |
||||
TargetArgumentInfo = targetArgumentInfo; |
||||
Target = target; |
||||
} |
||||
|
||||
public override void WriteTo(ITextOutput output, ILAstWritingOptions options) |
||||
{ |
||||
ILRange.WriteTo(output, options); |
||||
output.Write(OpCode); |
||||
WriteBinderFlags(output, options); |
||||
output.Write(' '); |
||||
output.Write(Name); |
||||
WriteArgumentList(output, options, (Target, TargetArgumentInfo)); |
||||
} |
||||
|
||||
public override StackType ResultType => StackType.O; |
||||
|
||||
public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) |
||||
{ |
||||
if (index != 0) |
||||
throw new ArgumentOutOfRangeException(nameof(index)); |
||||
return TargetArgumentInfo; |
||||
} |
||||
} |
||||
|
||||
partial class DynamicSetMemberInstruction |
||||
{ |
||||
public string Name { get; } |
||||
public CSharpArgumentInfo TargetArgumentInfo { get; } |
||||
public CSharpArgumentInfo ValueArgumentInfo { get; } |
||||
|
||||
public DynamicSetMemberInstruction(CSharpBinderFlags binderFlags, string name, IType context, CSharpArgumentInfo targetArgumentInfo, ILInstruction target, CSharpArgumentInfo valueArgumentInfo, ILInstruction value) |
||||
: base(OpCode.DynamicSetMemberInstruction, binderFlags, context) |
||||
{ |
||||
Name = name; |
||||
TargetArgumentInfo = targetArgumentInfo; |
||||
Target = target; |
||||
ValueArgumentInfo = valueArgumentInfo; |
||||
Value = value; |
||||
} |
||||
|
||||
public override void WriteTo(ITextOutput output, ILAstWritingOptions options) |
||||
{ |
||||
ILRange.WriteTo(output, options); |
||||
output.Write(OpCode); |
||||
WriteBinderFlags(output, options); |
||||
output.Write(' '); |
||||
output.Write(Name); |
||||
WriteArgumentList(output, options, (Target, TargetArgumentInfo), (Value, ValueArgumentInfo)); |
||||
} |
||||
|
||||
public override StackType ResultType => StackType.O; |
||||
|
||||
public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) |
||||
{ |
||||
switch (index) { |
||||
case 0: |
||||
return TargetArgumentInfo; |
||||
case 1: |
||||
return ValueArgumentInfo; |
||||
default: |
||||
throw new ArgumentOutOfRangeException(nameof(index)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
partial class DynamicGetIndexInstruction |
||||
{ |
||||
public IReadOnlyList<CSharpArgumentInfo> ArgumentInfo { get; } |
||||
|
||||
public DynamicGetIndexInstruction(CSharpBinderFlags binderFlags, IType context, CSharpArgumentInfo[] argumentInfo, ILInstruction[] arguments) |
||||
: base(OpCode.DynamicGetIndexInstruction, binderFlags, context) |
||||
{ |
||||
ArgumentInfo = argumentInfo; |
||||
Arguments = new InstructionCollection<ILInstruction>(this, 0); |
||||
Arguments.AddRange(arguments); |
||||
} |
||||
|
||||
public override void WriteTo(ITextOutput output, ILAstWritingOptions options) |
||||
{ |
||||
ILRange.WriteTo(output, options); |
||||
output.Write(OpCode); |
||||
WriteBinderFlags(output, options); |
||||
output.Write(' '); |
||||
output.Write("get_Item"); |
||||
WriteArgumentList(output, options, Arguments.Zip(ArgumentInfo)); |
||||
} |
||||
|
||||
public override StackType ResultType => StackType.O; |
||||
|
||||
public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) |
||||
{ |
||||
if (index < 0 || index >= ArgumentInfo.Count) |
||||
throw new ArgumentOutOfRangeException(nameof(index)); |
||||
return ArgumentInfo[index]; |
||||
} |
||||
} |
||||
|
||||
partial class DynamicSetIndexInstruction |
||||
{ |
||||
public IReadOnlyList<CSharpArgumentInfo> ArgumentInfo { get; } |
||||
|
||||
public DynamicSetIndexInstruction(CSharpBinderFlags binderFlags, IType context, CSharpArgumentInfo[] argumentInfo, ILInstruction[] arguments) |
||||
: base(OpCode.DynamicSetIndexInstruction, binderFlags, context) |
||||
{ |
||||
ArgumentInfo = argumentInfo; |
||||
Arguments = new InstructionCollection<ILInstruction>(this, 0); |
||||
Arguments.AddRange(arguments); |
||||
} |
||||
|
||||
public override void WriteTo(ITextOutput output, ILAstWritingOptions options) |
||||
{ |
||||
ILRange.WriteTo(output, options); |
||||
output.Write(OpCode); |
||||
WriteBinderFlags(output, options); |
||||
output.Write(' '); |
||||
output.Write("set_Item"); |
||||
WriteArgumentList(output, options, Arguments.Zip(ArgumentInfo)); |
||||
} |
||||
|
||||
public override StackType ResultType => StackType.O; |
||||
|
||||
public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) |
||||
{ |
||||
if (index < 0 || index >= ArgumentInfo.Count) |
||||
throw new ArgumentOutOfRangeException(nameof(index)); |
||||
return ArgumentInfo[index]; |
||||
} |
||||
} |
||||
|
||||
partial class DynamicInvokeConstructorInstruction |
||||
{ |
||||
public IReadOnlyList<CSharpArgumentInfo> ArgumentInfo { get; } |
||||
|
||||
public DynamicInvokeConstructorInstruction(CSharpBinderFlags binderFlags, IType context, CSharpArgumentInfo[] argumentInfo, ILInstruction[] arguments) |
||||
: base(OpCode.DynamicInvokeConstructorInstruction, binderFlags, context) |
||||
{ |
||||
ArgumentInfo = argumentInfo; |
||||
Arguments = new InstructionCollection<ILInstruction>(this, 0); |
||||
Arguments.AddRange(arguments); |
||||
} |
||||
|
||||
public override void WriteTo(ITextOutput output, ILAstWritingOptions options) |
||||
{ |
||||
ILRange.WriteTo(output, options); |
||||
output.Write(OpCode); |
||||
WriteBinderFlags(output, options); |
||||
output.Write(' '); |
||||
output.Write(".ctor"); |
||||
WriteArgumentList(output, options, Arguments.Zip(ArgumentInfo)); |
||||
} |
||||
|
||||
public override StackType ResultType => StackType.O; |
||||
|
||||
public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) |
||||
{ |
||||
if (index < 0 || index >= ArgumentInfo.Count) |
||||
throw new ArgumentOutOfRangeException(nameof(index)); |
||||
return ArgumentInfo[index]; |
||||
} |
||||
} |
||||
|
||||
partial class DynamicBinaryOperatorInstruction |
||||
{ |
||||
public CSharpArgumentInfo LeftArgumentInfo { get; } |
||||
public CSharpArgumentInfo RightArgumentInfo { get; } |
||||
public ExpressionType Operation { get; } |
||||
|
||||
public DynamicBinaryOperatorInstruction(CSharpBinderFlags binderFlags, ExpressionType operation, IType context, CSharpArgumentInfo leftArgumentInfo, ILInstruction left, CSharpArgumentInfo rightArgumentInfo, ILInstruction right) |
||||
: base(OpCode.DynamicBinaryOperatorInstruction, binderFlags, context) |
||||
{ |
||||
Operation = operation; |
||||
LeftArgumentInfo = leftArgumentInfo; |
||||
Left = left; |
||||
RightArgumentInfo = rightArgumentInfo; |
||||
Right = right; |
||||
} |
||||
|
||||
public override void WriteTo(ITextOutput output, ILAstWritingOptions options) |
||||
{ |
||||
ILRange.WriteTo(output, options); |
||||
output.Write(OpCode); |
||||
WriteBinderFlags(output, options); |
||||
output.Write(' '); |
||||
output.Write(Operation.ToString()); |
||||
WriteArgumentList(output, options, (Left, LeftArgumentInfo), (Right, RightArgumentInfo)); |
||||
} |
||||
|
||||
public override StackType ResultType => StackType.O; |
||||
|
||||
public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) |
||||
{ |
||||
switch (index) { |
||||
case 0: |
||||
return LeftArgumentInfo; |
||||
case 1: |
||||
return RightArgumentInfo; |
||||
default: |
||||
throw new ArgumentOutOfRangeException(nameof(index)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
partial class DynamicUnaryOperatorInstruction |
||||
{ |
||||
public CSharpArgumentInfo OperandArgumentInfo { get; } |
||||
public ExpressionType Operation { get; } |
||||
|
||||
public DynamicUnaryOperatorInstruction(CSharpBinderFlags binderFlags, ExpressionType operation, IType context, CSharpArgumentInfo operandArgumentInfo, ILInstruction operand) |
||||
: base(OpCode.DynamicUnaryOperatorInstruction, binderFlags, context) |
||||
{ |
||||
Operation = operation; |
||||
OperandArgumentInfo = operandArgumentInfo; |
||||
Operand = operand; |
||||
} |
||||
|
||||
public override void WriteTo(ITextOutput output, ILAstWritingOptions options) |
||||
{ |
||||
ILRange.WriteTo(output, options); |
||||
output.Write(OpCode); |
||||
WriteBinderFlags(output, options); |
||||
output.Write(' '); |
||||
output.Write(Operation.ToString()); |
||||
WriteArgumentList(output, options, (Operand, OperandArgumentInfo)); |
||||
} |
||||
|
||||
public override StackType ResultType { |
||||
get { |
||||
switch (Operation) { |
||||
case ExpressionType.IsFalse: |
||||
case ExpressionType.IsTrue: |
||||
return StackType.I4; // bool
|
||||
default: |
||||
return StackType.O; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) |
||||
{ |
||||
switch (index) { |
||||
case 0: |
||||
return OperandArgumentInfo; |
||||
default: |
||||
throw new ArgumentOutOfRangeException(nameof(index)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
partial class DynamicInvokeInstruction |
||||
{ |
||||
public IReadOnlyList<CSharpArgumentInfo> ArgumentInfo { get; } |
||||
|
||||
public DynamicInvokeInstruction(CSharpBinderFlags binderFlags, IType context, CSharpArgumentInfo[] argumentInfo, ILInstruction[] arguments) |
||||
: base(OpCode.DynamicInvokeInstruction, binderFlags, context) |
||||
{ |
||||
ArgumentInfo = argumentInfo; |
||||
Arguments = new InstructionCollection<ILInstruction>(this, 0); |
||||
Arguments.AddRange(arguments); |
||||
} |
||||
|
||||
public override void WriteTo(ITextOutput output, ILAstWritingOptions options) |
||||
{ |
||||
ILRange.WriteTo(output, options); |
||||
output.Write(OpCode); |
||||
WriteBinderFlags(output, options); |
||||
output.Write(' '); |
||||
WriteArgumentList(output, options, Arguments.Zip(ArgumentInfo)); |
||||
} |
||||
|
||||
public override StackType ResultType => StackType.O; |
||||
|
||||
public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) |
||||
{ |
||||
if (index < 0 || index >= ArgumentInfo.Count) |
||||
throw new ArgumentOutOfRangeException(nameof(index)); |
||||
return ArgumentInfo[index]; |
||||
} |
||||
} |
||||
|
||||
partial class DynamicIsEventInstruction |
||||
{ |
||||
public string Name { get; } |
||||
|
||||
public DynamicIsEventInstruction(CSharpBinderFlags binderFlags, string name, IType context, ILInstruction argument) |
||||
: base(OpCode.DynamicIsEventInstruction, binderFlags, context) |
||||
{ |
||||
Name = name; |
||||
Argument = argument; |
||||
} |
||||
|
||||
public override void WriteTo(ITextOutput output, ILAstWritingOptions options) |
||||
{ |
||||
ILRange.WriteTo(output, options); |
||||
output.Write(OpCode); |
||||
WriteBinderFlags(output, options); |
||||
output.Write(' '); |
||||
output.Write('('); |
||||
Argument.WriteTo(output, options); |
||||
output.Write(')'); |
||||
} |
||||
|
||||
public override StackType ResultType => StackType.I4; |
||||
|
||||
public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) |
||||
{ |
||||
return default(CSharpArgumentInfo); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,572 @@
@@ -0,0 +1,572 @@
|
||||
// Copyright (c) 2018 Siegfried Pammer
|
||||
//
|
||||
// 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.Linq.Expressions; |
||||
using ICSharpCode.Decompiler.TypeSystem; |
||||
using ICSharpCode.Decompiler.Util; |
||||
|
||||
namespace ICSharpCode.Decompiler.IL.Transforms |
||||
{ |
||||
/// <summary>
|
||||
/// Transforms the "callsite initialization pattern" into DynamicInstructions.
|
||||
/// </summary>
|
||||
public class DynamicCallSiteTransform : IILTransform |
||||
{ |
||||
ILTransformContext context; |
||||
|
||||
const string CallSiteTypeName = "System.Runtime.CompilerServices.CallSite"; |
||||
const string CSharpBinderTypeName = "Microsoft.CSharp.RuntimeBinder.Binder"; |
||||
|
||||
public void Run(ILFunction function, ILTransformContext context) |
||||
{ |
||||
if (!context.Settings.Dynamic) |
||||
return; |
||||
|
||||
this.context = context; |
||||
|
||||
Dictionary<IField, CallSiteInfo> callsites = new Dictionary<IField, CallSiteInfo>(); |
||||
HashSet<BlockContainer> modifiedContainers = new HashSet<BlockContainer>(); |
||||
|
||||
foreach (var block in function.Descendants.OfType<Block>()) { |
||||
if (block.Instructions.Count < 2) continue; |
||||
// Check if, we deal with a callsite cache field null check:
|
||||
// if (comp(ldsfld <>p__3 == ldnull)) br IL_000c
|
||||
// br IL_002b
|
||||
if (!(block.Instructions.SecondToLastOrDefault() is IfInstruction ifInst)) continue; |
||||
if (!(block.Instructions.LastOrDefault() is Branch branchAfterInit)) continue; |
||||
if (!MatchCallSiteCacheNullCheck(ifInst.Condition, out var callSiteCacheField, out var callSiteDelegate, out bool invertBranches)) |
||||
continue; |
||||
if (!ifInst.TrueInst.MatchBranch(out var trueBlock)) |
||||
continue; |
||||
Block callSiteInitBlock, targetBlockAfterInit; |
||||
if (invertBranches) { |
||||
callSiteInitBlock = branchAfterInit.TargetBlock; |
||||
targetBlockAfterInit = trueBlock; |
||||
} else { |
||||
callSiteInitBlock = trueBlock; |
||||
targetBlockAfterInit = branchAfterInit.TargetBlock; |
||||
} |
||||
if (!ScanCallSiteInitBlock(callSiteInitBlock, callSiteCacheField, callSiteDelegate, out var callSiteInfo, out var blockAfterInit)) |
||||
continue; |
||||
if (targetBlockAfterInit != blockAfterInit) |
||||
continue; |
||||
callSiteInfo.DelegateType = callSiteDelegate; |
||||
callSiteInfo.ConditionalJumpToInit = ifInst; |
||||
callSiteInfo.Inverted = invertBranches; |
||||
callSiteInfo.BranchAfterInit = branchAfterInit; |
||||
callsites.Add(callSiteCacheField, callSiteInfo); |
||||
} |
||||
|
||||
var storesToRemove = new List<StLoc>(); |
||||
|
||||
foreach (var invokeCall in function.Descendants.OfType<CallVirt>()) { |
||||
if (invokeCall.Method.DeclaringType.Kind != TypeKind.Delegate || invokeCall.Method.Name != "Invoke" || invokeCall.Arguments.Count == 0) |
||||
continue; |
||||
var firstArgument = invokeCall.Arguments[0]; |
||||
if (firstArgument.MatchLdLoc(out var stackSlot) && stackSlot.Kind == VariableKind.StackSlot && stackSlot.IsSingleDefinition) { |
||||
firstArgument = ((StLoc)stackSlot.StoreInstructions[0]).Value; |
||||
} |
||||
if (!firstArgument.MatchLdFld(out var cacheFieldLoad, out var targetField)) |
||||
continue; |
||||
if (!cacheFieldLoad.MatchLdsFld(out var cacheField)) |
||||
continue; |
||||
if (!callsites.TryGetValue(cacheField, out var callsite)) |
||||
continue; |
||||
context.Stepper.Step("Transform callsite for " + callsite.MemberName); |
||||
var deadArguments = new List<ILInstruction>(); |
||||
ILInstruction replacement = MakeDynamicInstruction(callsite, invokeCall, deadArguments); |
||||
if (replacement == null) |
||||
continue; |
||||
invokeCall.ReplaceWith(replacement); |
||||
Debug.Assert(callsite.ConditionalJumpToInit?.Parent is Block); |
||||
var block = ((Block)callsite.ConditionalJumpToInit.Parent); |
||||
if (callsite.Inverted) { |
||||
block.Instructions.Remove(callsite.ConditionalJumpToInit); |
||||
callsite.BranchAfterInit.ReplaceWith(callsite.ConditionalJumpToInit.TrueInst); |
||||
} else { |
||||
block.Instructions.Remove(callsite.ConditionalJumpToInit); |
||||
} |
||||
foreach (var arg in deadArguments) { |
||||
if (arg.MatchLdLoc(out var temporary) && temporary.Kind == VariableKind.StackSlot && temporary.IsSingleDefinition && temporary.LoadCount == 0) { |
||||
StLoc stLoc = (StLoc)temporary.StoreInstructions[0]; |
||||
if (stLoc.Parent is Block storeParentBlock) { |
||||
var value = stLoc.Value; |
||||
if (value.MatchLdsFld(out var cacheFieldCopy) && cacheFieldCopy.Equals(cacheField)) |
||||
storesToRemove.Add(stLoc); |
||||
if (value.MatchLdFld(out cacheFieldLoad, out var targetFieldCopy) && cacheFieldLoad.MatchLdsFld(out cacheFieldCopy) && cacheField.Equals(cacheFieldCopy) && targetField.Equals(targetFieldCopy)) |
||||
storesToRemove.Add(stLoc); |
||||
} |
||||
} |
||||
} |
||||
modifiedContainers.Add((BlockContainer)block.Parent); |
||||
} |
||||
|
||||
foreach (var inst in storesToRemove) { |
||||
Block parentBlock = (Block)inst.Parent; |
||||
parentBlock.Instructions.RemoveAt(inst.ChildIndex); |
||||
} |
||||
|
||||
foreach (var container in modifiedContainers) |
||||
container.SortBlocks(deleteUnreachableBlocks: true); |
||||
} |
||||
|
||||
ILInstruction MakeDynamicInstruction(CallSiteInfo callsite, CallVirt targetInvokeCall, List<ILInstruction> deadArguments) |
||||
{ |
||||
switch (callsite.Kind) { |
||||
case BinderMethodKind.BinaryOperation: |
||||
deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); |
||||
return new DynamicBinaryOperatorInstruction( |
||||
binderFlags: callsite.Flags, |
||||
operation: callsite.Operation, |
||||
context: callsite.Context, |
||||
leftArgumentInfo: callsite.ArgumentInfos[0], |
||||
left: targetInvokeCall.Arguments[2], |
||||
rightArgumentInfo: callsite.ArgumentInfos[1], |
||||
right: targetInvokeCall.Arguments[3] |
||||
); |
||||
case BinderMethodKind.Convert: |
||||
deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); |
||||
ILInstruction result = new DynamicConvertInstruction( |
||||
binderFlags: callsite.Flags, |
||||
context: callsite.Context, |
||||
type: callsite.ConvertTargetType, |
||||
argument: targetInvokeCall.Arguments[2] |
||||
); |
||||
if (result.ResultType == StackType.Unknown) { |
||||
// if references are missing, we need to coerce the primitive type to None.
|
||||
// Otherwise we will get loads of assertions.
|
||||
result = new Conv(result, PrimitiveType.None, ((DynamicConvertInstruction)result).IsChecked, Sign.None); |
||||
} |
||||
return result; |
||||
case BinderMethodKind.GetIndex: |
||||
deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); |
||||
return new DynamicGetIndexInstruction( |
||||
binderFlags: callsite.Flags, |
||||
context: callsite.Context, |
||||
argumentInfo: callsite.ArgumentInfos, |
||||
arguments: targetInvokeCall.Arguments.Skip(2).ToArray() |
||||
); |
||||
case BinderMethodKind.GetMember: |
||||
deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); |
||||
return new DynamicGetMemberInstruction( |
||||
binderFlags: callsite.Flags, |
||||
name: callsite.MemberName, |
||||
context: callsite.Context, |
||||
targetArgumentInfo: callsite.ArgumentInfos[0], |
||||
target: targetInvokeCall.Arguments[2] |
||||
); |
||||
case BinderMethodKind.Invoke: |
||||
deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); |
||||
return new DynamicInvokeInstruction( |
||||
binderFlags: callsite.Flags, |
||||
context: callsite.Context, |
||||
argumentInfo: callsite.ArgumentInfos, |
||||
arguments: targetInvokeCall.Arguments.Skip(2).ToArray() |
||||
); |
||||
case BinderMethodKind.InvokeConstructor: |
||||
deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); |
||||
return new DynamicInvokeConstructorInstruction( |
||||
binderFlags: callsite.Flags, |
||||
context: callsite.Context, |
||||
argumentInfo: callsite.ArgumentInfos, |
||||
arguments: targetInvokeCall.Arguments.Skip(2).ToArray() |
||||
); |
||||
case BinderMethodKind.InvokeMember: |
||||
deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); |
||||
return new DynamicInvokeMemberInstruction( |
||||
binderFlags: callsite.Flags, |
||||
name: callsite.MemberName, |
||||
typeArguments: callsite.TypeArguments, |
||||
context: callsite.Context, |
||||
argumentInfo: callsite.ArgumentInfos, |
||||
arguments: targetInvokeCall.Arguments.Skip(2).ToArray() |
||||
); |
||||
case BinderMethodKind.IsEvent: |
||||
deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); |
||||
return new DynamicIsEventInstruction( |
||||
binderFlags: callsite.Flags, |
||||
name: callsite.MemberName, |
||||
context: callsite.Context, |
||||
argument: targetInvokeCall.Arguments[2] |
||||
); |
||||
case BinderMethodKind.SetIndex: |
||||
deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); |
||||
return new DynamicSetIndexInstruction( |
||||
binderFlags: callsite.Flags, |
||||
context: callsite.Context, |
||||
argumentInfo: callsite.ArgumentInfos, |
||||
arguments: targetInvokeCall.Arguments.Skip(2).ToArray() |
||||
); |
||||
case BinderMethodKind.SetMember: |
||||
deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); |
||||
return new DynamicSetMemberInstruction( |
||||
binderFlags: callsite.Flags, |
||||
name: callsite.MemberName, |
||||
context: callsite.Context, |
||||
targetArgumentInfo: callsite.ArgumentInfos[0], |
||||
target: targetInvokeCall.Arguments[2], |
||||
valueArgumentInfo: callsite.ArgumentInfos[1], |
||||
value: targetInvokeCall.Arguments[3] |
||||
); |
||||
case BinderMethodKind.UnaryOperation: |
||||
deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); |
||||
return new DynamicUnaryOperatorInstruction( |
||||
binderFlags: callsite.Flags, |
||||
operation: callsite.Operation, |
||||
context: callsite.Context, |
||||
operandArgumentInfo: callsite.ArgumentInfos[0], |
||||
operand: targetInvokeCall.Arguments[2] |
||||
); |
||||
default: |
||||
throw new NotSupportedException(); |
||||
} |
||||
throw new NotImplementedException(); |
||||
} |
||||
|
||||
bool ScanCallSiteInitBlock(Block callSiteInitBlock, IField callSiteCacheField, IType callSiteDelegateType, out CallSiteInfo callSiteInfo, out Block blockAfterInit) |
||||
{ |
||||
callSiteInfo = default(CallSiteInfo); |
||||
blockAfterInit = null; |
||||
int instCount = callSiteInitBlock.Instructions.Count; |
||||
if (callSiteInitBlock.IncomingEdgeCount != 1 || instCount < 2) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[instCount - 1].MatchBranch(out blockAfterInit)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[instCount - 2].MatchStsFld(out var field, out var value) || !field.Equals(callSiteCacheField)) |
||||
return false; |
||||
if (!(value is Call createBinderCall) || createBinderCall.Method.TypeArguments.Count != 0 || createBinderCall.Arguments.Count != 1 || createBinderCall.Method.Name != "Create" || createBinderCall.Method.DeclaringType.FullName != CallSiteTypeName || createBinderCall.Method.DeclaringType.TypeArguments.Count != 1) |
||||
return false; |
||||
if (!(createBinderCall.Arguments[0] is Call binderCall) || binderCall.Method.DeclaringType.FullName != CSharpBinderTypeName || binderCall.Method.DeclaringType.TypeParameterCount != 0) |
||||
return false; |
||||
callSiteInfo.DelegateType = callSiteDelegateType; |
||||
callSiteInfo.InitBlock = callSiteInitBlock; |
||||
switch (binderCall.Method.Name) { |
||||
case "IsEvent": |
||||
callSiteInfo.Kind = BinderMethodKind.IsEvent; |
||||
// In the case of Binder.IsEvent all arguments should already be properly inlined, as there is no array initializer:
|
||||
// Scan arguments: binder flags, member name, context type
|
||||
if (binderCall.Arguments.Count != 3) |
||||
return false; |
||||
if (!binderCall.Arguments[0].MatchLdcI4(out int binderFlagsInteger)) |
||||
return false; |
||||
callSiteInfo.Flags = (CSharpBinderFlags)binderFlagsInteger; |
||||
if (!binderCall.Arguments[1].MatchLdStr(out string name)) |
||||
return false; |
||||
callSiteInfo.MemberName = name; |
||||
if (!TransformExpressionTrees.MatchGetTypeFromHandle(binderCall.Arguments[2], out var contextType)) |
||||
return false; |
||||
callSiteInfo.Context = contextType; |
||||
return true; |
||||
case "Convert": |
||||
callSiteInfo.Kind = BinderMethodKind.Convert; |
||||
// In the case of Binder.Convert all arguments should already be properly inlined, as there is no array initializer:
|
||||
// Scan arguments: binder flags, target type, context type
|
||||
if (binderCall.Arguments.Count != 3) |
||||
return false; |
||||
if (!binderCall.Arguments[0].MatchLdcI4(out binderFlagsInteger)) |
||||
return false; |
||||
callSiteInfo.Flags = (CSharpBinderFlags)binderFlagsInteger; |
||||
if (!TransformExpressionTrees.MatchGetTypeFromHandle(binderCall.Arguments[1], out var targetType)) |
||||
return false; |
||||
callSiteInfo.ConvertTargetType = targetType; |
||||
if (!TransformExpressionTrees.MatchGetTypeFromHandle(binderCall.Arguments[2], out contextType)) |
||||
return false; |
||||
callSiteInfo.Context = contextType; |
||||
return true; |
||||
case "InvokeMember": |
||||
callSiteInfo.Kind = BinderMethodKind.InvokeMember; |
||||
if (binderCall.Arguments.Count != 5) |
||||
return false; |
||||
// First argument: binder flags
|
||||
// The value must be a single ldc.i4 instruction.
|
||||
if (!binderCall.Arguments[0].MatchLdLoc(out var variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[0].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!value.MatchLdcI4(out binderFlagsInteger)) |
||||
return false; |
||||
callSiteInfo.Flags = (CSharpBinderFlags)binderFlagsInteger; |
||||
// Second argument: method name
|
||||
// The value must be a single ldstr instruction.
|
||||
if (!binderCall.Arguments[1].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[1].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!value.MatchLdStr(out name)) |
||||
return false; |
||||
callSiteInfo.MemberName = name; |
||||
// Third argument: type arguments
|
||||
// The value must be either ldnull (no type arguments) or an array initializer pattern.
|
||||
if (!binderCall.Arguments[2].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[2].MatchStLoc(out var variableOrTemporary, out value)) |
||||
return false; |
||||
int numberOfTypeArguments = 0; |
||||
if (!value.MatchLdNull()) { |
||||
if (value is NewArr typeArgsNewArr && typeArgsNewArr.Type.IsKnownType(KnownTypeCode.Type) && typeArgsNewArr.Indices.Count == 1 && typeArgsNewArr.Indices[0].MatchLdcI4(out numberOfTypeArguments)) { |
||||
if (!TransformArrayInitializers.HandleSimpleArrayInitializer(callSiteInitBlock, 3, variableOrTemporary, typeArgsNewArr.Type, numberOfTypeArguments, out var typeArguments, out _)) |
||||
return false; |
||||
int i = 0; |
||||
callSiteInfo.TypeArguments = new IType[numberOfTypeArguments]; |
||||
foreach (var typeArg in typeArguments) { |
||||
if (!TransformExpressionTrees.MatchGetTypeFromHandle(typeArg, out var type)) |
||||
return false; |
||||
callSiteInfo.TypeArguments[i] = type; |
||||
i++; |
||||
} |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
int typeArgumentsOffset = numberOfTypeArguments; |
||||
// Special case for csc array initializers:
|
||||
if (variableOrTemporary != variable) { |
||||
// store temporary from array initializer in variable
|
||||
if (!callSiteInitBlock.Instructions[3 + typeArgumentsOffset].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!value.MatchLdLoc(variableOrTemporary)) |
||||
return false; |
||||
typeArgumentsOffset++; |
||||
} |
||||
// Fourth argument: context type
|
||||
if (!binderCall.Arguments[3].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[3 + typeArgumentsOffset].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!TransformExpressionTrees.MatchGetTypeFromHandle(value, out contextType)) |
||||
return false; |
||||
callSiteInfo.Context = contextType; |
||||
// Fifth argument: call parameter info
|
||||
if (!binderCall.Arguments[4].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[4 + typeArgumentsOffset].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!ExtractArgumentInfo(value, ref callSiteInfo, 5 + typeArgumentsOffset, variable)) |
||||
return false; |
||||
return true; |
||||
case "GetMember": |
||||
case "SetMember": |
||||
callSiteInfo.Kind = binderCall.Method.Name == "GetMember" ? BinderMethodKind.GetMember : BinderMethodKind.SetMember; |
||||
if (binderCall.Arguments.Count != 4) |
||||
return false; |
||||
// First argument: binder flags
|
||||
// The value must be a single ldc.i4 instruction.
|
||||
if (!binderCall.Arguments[0].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[0].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!value.MatchLdcI4(out binderFlagsInteger)) |
||||
return false; |
||||
callSiteInfo.Flags = (CSharpBinderFlags)binderFlagsInteger; |
||||
// Second argument: method name
|
||||
// The value must be a single ldstr instruction.
|
||||
if (!binderCall.Arguments[1].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[1].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!value.MatchLdStr(out name)) |
||||
return false; |
||||
callSiteInfo.MemberName = name; |
||||
// Third argument: context type
|
||||
if (!binderCall.Arguments[2].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[2].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!TransformExpressionTrees.MatchGetTypeFromHandle(value, out contextType)) |
||||
return false; |
||||
callSiteInfo.Context = contextType; |
||||
// Fourth argument: call parameter info
|
||||
if (!binderCall.Arguments[3].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[3].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!ExtractArgumentInfo(value, ref callSiteInfo, 4, variable)) |
||||
return false; |
||||
return true; |
||||
case "GetIndex": |
||||
case "SetIndex": |
||||
case "InvokeConstructor": |
||||
case "Invoke": |
||||
switch (binderCall.Method.Name) { |
||||
case "GetIndex": |
||||
callSiteInfo.Kind = BinderMethodKind.GetIndex; |
||||
break; |
||||
case "SetIndex": |
||||
callSiteInfo.Kind = BinderMethodKind.SetIndex; |
||||
break; |
||||
case "InvokeConstructor": |
||||
callSiteInfo.Kind = BinderMethodKind.InvokeConstructor; |
||||
break; |
||||
case "Invoke": |
||||
callSiteInfo.Kind = BinderMethodKind.Invoke; |
||||
break; |
||||
default: |
||||
throw new NotSupportedException(); |
||||
} |
||||
if (binderCall.Arguments.Count != 3) |
||||
return false; |
||||
// First argument: binder flags
|
||||
// The value must be a single ldc.i4 instruction.
|
||||
if (!binderCall.Arguments[0].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[0].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!value.MatchLdcI4(out binderFlagsInteger)) |
||||
return false; |
||||
callSiteInfo.Flags = (CSharpBinderFlags)binderFlagsInteger; |
||||
// Second argument: context type
|
||||
if (!binderCall.Arguments[1].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[1].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!TransformExpressionTrees.MatchGetTypeFromHandle(value, out contextType)) |
||||
return false; |
||||
callSiteInfo.Context = contextType; |
||||
// Third argument: call parameter info
|
||||
if (!binderCall.Arguments[2].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[2].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!ExtractArgumentInfo(value, ref callSiteInfo, 3, variable)) |
||||
return false; |
||||
return true; |
||||
case "UnaryOperation": |
||||
case "BinaryOperation": |
||||
callSiteInfo.Kind = binderCall.Method.Name == "BinaryOperation" ? BinderMethodKind.BinaryOperation : BinderMethodKind.UnaryOperation; |
||||
if (binderCall.Arguments.Count != 4) |
||||
return false; |
||||
// First argument: binder flags
|
||||
// The value must be a single ldc.i4 instruction.
|
||||
if (!binderCall.Arguments[0].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[0].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!value.MatchLdcI4(out binderFlagsInteger)) |
||||
return false; |
||||
callSiteInfo.Flags = (CSharpBinderFlags)binderFlagsInteger; |
||||
// Second argument: operation
|
||||
// The value must be a single ldc.i4 instruction.
|
||||
if (!binderCall.Arguments[1].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[1].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!value.MatchLdcI4(out int operation)) |
||||
return false; |
||||
callSiteInfo.Operation = (ExpressionType)operation; |
||||
// Third argument: context type
|
||||
if (!binderCall.Arguments[2].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[2].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!TransformExpressionTrees.MatchGetTypeFromHandle(value, out contextType)) |
||||
return false; |
||||
callSiteInfo.Context = contextType; |
||||
// Fourth argument: call parameter info
|
||||
if (!binderCall.Arguments[3].MatchLdLoc(out variable)) |
||||
return false; |
||||
if (!callSiteInitBlock.Instructions[3].MatchStLoc(variable, out value)) |
||||
return false; |
||||
if (!ExtractArgumentInfo(value, ref callSiteInfo, 4, variable)) |
||||
return false; |
||||
return true; |
||||
default: |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
bool ExtractArgumentInfo(ILInstruction value, ref CallSiteInfo callSiteInfo, int instructionOffset, ILVariable variable) |
||||
{ |
||||
if (!(value is NewArr newArr2 && newArr2.Type.FullName == "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo" && newArr2.Indices.Count == 1 && newArr2.Indices[0].MatchLdcI4(out var numberOfArguments))) |
||||
return false; |
||||
if (!TransformArrayInitializers.HandleSimpleArrayInitializer(callSiteInfo.InitBlock, instructionOffset, variable, newArr2.Type, numberOfArguments, out var arguments, out _)) |
||||
return false; |
||||
int i = 0; |
||||
callSiteInfo.ArgumentInfos = new CSharpArgumentInfo[numberOfArguments]; |
||||
var compileTimeTypes = callSiteInfo.DelegateType.GetDelegateInvokeMethod().Parameters.SelectArray(p => p.Type); |
||||
foreach (var arg in arguments) { |
||||
if (!(arg is Call createCall)) |
||||
return false; |
||||
if (!(createCall.Method.Name == "Create" && createCall.Method.DeclaringType.FullName == "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo" && createCall.Arguments.Count == 2)) |
||||
return false; |
||||
if (!createCall.Arguments[0].MatchLdcI4(out var argumentInfoFlags)) |
||||
return false; |
||||
string argumentName = null; |
||||
if (!createCall.Arguments[1].MatchLdStr(out argumentName)) |
||||
if (!createCall.Arguments[1].MatchLdNull()) |
||||
return false; |
||||
callSiteInfo.ArgumentInfos[i] = new CSharpArgumentInfo { Flags = (CSharpArgumentInfoFlags)argumentInfoFlags, Name = argumentName, CompileTimeType = compileTimeTypes[i + 1] }; |
||||
i++; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
bool MatchCallSiteCacheNullCheck(ILInstruction condition, out IField callSiteCacheField, out IType callSiteDelegate, out bool invertBranches) |
||||
{ |
||||
callSiteCacheField = null; |
||||
callSiteDelegate = null; |
||||
invertBranches = false; |
||||
if (!condition.MatchCompEqualsNull(out var argument)) { |
||||
if (!condition.MatchCompNotEqualsNull(out argument)) |
||||
return false; |
||||
invertBranches = true; |
||||
} |
||||
if (!argument.MatchLdsFld(out callSiteCacheField) || callSiteCacheField.ReturnType.TypeArguments.Count != 1 || callSiteCacheField.ReturnType.FullName != CallSiteTypeName) |
||||
return false; |
||||
callSiteDelegate = callSiteCacheField.ReturnType.TypeArguments[0]; |
||||
if (callSiteDelegate.Kind != TypeKind.Delegate) |
||||
return false; |
||||
return true; |
||||
} |
||||
|
||||
struct CallSiteInfo |
||||
{ |
||||
public bool Inverted; |
||||
public ILInstruction BranchAfterInit; |
||||
public IfInstruction ConditionalJumpToInit; |
||||
public Block InitBlock; |
||||
public IType DelegateType; |
||||
public BinderMethodKind Kind; |
||||
public CSharpBinderFlags Flags; |
||||
public ExpressionType Operation; |
||||
public IType Context; |
||||
public IType ConvertTargetType; |
||||
public IType[] TypeArguments; |
||||
public CSharpArgumentInfo[] ArgumentInfos; |
||||
public string MemberName; |
||||
} |
||||
|
||||
enum BinderMethodKind |
||||
{ |
||||
BinaryOperation, |
||||
Convert, |
||||
GetIndex, |
||||
GetMember, |
||||
Invoke, |
||||
InvokeConstructor, |
||||
InvokeMember, |
||||
IsEvent, |
||||
SetIndex, |
||||
SetMember, |
||||
UnaryOperation |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,51 @@
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2018 Siegfried Pammer
|
||||
//
|
||||
// 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.Text; |
||||
using ICSharpCode.Decompiler.IL.Transforms; |
||||
using ICSharpCode.Decompiler.TypeSystem; |
||||
|
||||
namespace ICSharpCode.Decompiler.IL |
||||
{ |
||||
class IntroduceDynamicTypeOnLocals : IILTransform |
||||
{ |
||||
public void Run(ILFunction function, ILTransformContext context) |
||||
{ |
||||
foreach (var variable in function.Variables) { |
||||
if (variable.Kind != VariableKind.Local && |
||||
variable.Kind != VariableKind.StackSlot && |
||||
variable.Kind != VariableKind.ForeachLocal && |
||||
variable.Kind != VariableKind.UsingLocal) { |
||||
continue; |
||||
} |
||||
if (!variable.Type.IsKnownType(KnownTypeCode.Object) || variable.LoadCount == 0) |
||||
continue; |
||||
foreach (var load in variable.LoadInstructions) { |
||||
if (load.Parent is DynamicInstruction dynamicInstruction) { |
||||
var argumentInfo = dynamicInstruction.GetArgumentInfoOfChild(load.ChildIndex); |
||||
if (!argumentInfo.HasFlag(CSharpArgumentInfoFlags.UseCompileTimeType)) { |
||||
variable.Type = SpecialType.Dynamic; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue