Browse Source

TransformCollectionAndObjectInitializers: Fix problems with unknown types.

pull/734/merge
Siegfried Pammer 8 years ago
parent
commit
934edb6e79
  1. 2
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  2. 29
      ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs

2
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1445,7 +1445,7 @@ namespace ICSharpCode.Decompiler.CSharp
elementsStack.Push(elements); elementsStack.Push(elements);
List<AccessPathElement> currentPath = null; List<AccessPathElement> currentPath = null;
foreach (var inst in block.Instructions.Skip(1)) { foreach (var inst in block.Instructions.Skip(1)) {
var info = AccessPathElement.GetAccessPath(inst); var info = AccessPathElement.GetAccessPath(inst, initObjRR.Type);
if (info.Kind == AccessPathKind.Invalid) continue; if (info.Kind == AccessPathKind.Invalid) continue;
if (currentPath == null) { if (currentPath == null) {
currentPath = info.Path; currentPath = info.Path;

29
ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs

@ -45,20 +45,30 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// Match stloc(v, newobj) // Match stloc(v, newobj)
if (inst.MatchStLoc(out var v, out var initInst) && (v.Kind == VariableKind.Local || v.Kind == VariableKind.StackSlot)) { if (inst.MatchStLoc(out var v, out var initInst) && (v.Kind == VariableKind.Local || v.Kind == VariableKind.StackSlot)) {
Block initializerBlock = null; Block initializerBlock = null;
IType instType;
switch (initInst) { switch (initInst) {
case NewObj newObjInst: case NewObj newObjInst:
if (newObjInst.ILStackWasEmpty && !context.Function.Method.IsConstructor)
return false;
// Do not try to transform display class usages or delegate construction. // Do not try to transform display class usages or delegate construction.
// DelegateConstruction transform cannot deal with this. // DelegateConstruction transform cannot deal with this.
if (DelegateConstruction.IsSimpleDisplayClass(newObjInst.Method.DeclaringType)) if (DelegateConstruction.IsSimpleDisplayClass(newObjInst.Method.DeclaringType))
return false; return false;
if (DelegateConstruction.IsDelegateConstruction(newObjInst) || DelegateConstruction.IsPotentialClosure(context, newObjInst)) if (DelegateConstruction.IsDelegateConstruction(newObjInst) || DelegateConstruction.IsPotentialClosure(context, newObjInst))
return false; return false;
instType = newObjInst.Method.DeclaringType;
break; break;
case DefaultValue defaultVal: case DefaultValue defaultVal:
instType = defaultVal.Type;
break; break;
case Block existingInitializer: case Block existingInitializer:
if (existingInitializer.Type == BlockType.CollectionInitializer || existingInitializer.Type == BlockType.ObjectInitializer) { if (existingInitializer.Type == BlockType.CollectionInitializer || existingInitializer.Type == BlockType.ObjectInitializer) {
initializerBlock = existingInitializer; initializerBlock = existingInitializer;
var value = ((StLoc)initializerBlock.Instructions[0]).Value;
if (value is NewObj no)
instType = no.Method.DeclaringType;
else
instType = ((DefaultValue)value).Type;
break; break;
} }
return false; return false;
@ -73,7 +83,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// if the method is a setter we're dealing with an object initializer // if the method is a setter we're dealing with an object initializer
// if the method is named Add and has at least 2 arguments we're dealing with a collection/dictionary initializer // if the method is named Add and has at least 2 arguments we're dealing with a collection/dictionary initializer
while (pos + initializerItemsCount + 1 < body.Instructions.Count while (pos + initializerItemsCount + 1 < body.Instructions.Count
&& IsPartOfInitializer(body.Instructions, pos + initializerItemsCount + 1, v, ref blockType)) && IsPartOfInitializer(body.Instructions, pos + initializerItemsCount + 1, v, instType, ref blockType))
initializerItemsCount++; initializerItemsCount++;
if (initializerItemsCount == 0) if (initializerItemsCount == 0)
return false; return false;
@ -114,9 +124,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true; return true;
} }
bool IsPartOfInitializer(InstructionCollection<ILInstruction> instructions, int pos, ILVariable target, ref BlockType blockType) bool IsPartOfInitializer(InstructionCollection<ILInstruction> instructions, int pos, ILVariable target, IType rootType, ref BlockType blockType)
{ {
(var kind, var path, var values, var targetVariable) = AccessPathElement.GetAccessPath(instructions[pos]); (var kind, var path, var values, var targetVariable) = AccessPathElement.GetAccessPath(instructions[pos], rootType);
switch (kind) { switch (kind) {
case AccessPathKind.Adder: case AccessPathKind.Adder:
return target == targetVariable; return target == targetVariable;
@ -152,7 +162,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public override string ToString() => $"[{Member}, {Index}]"; public override string ToString() => $"[{Member}, {Index}]";
public static (AccessPathKind Kind, List<AccessPathElement> Path, List<ILInstruction> Values, ILVariable Target) GetAccessPath(ILInstruction instruction) public static (AccessPathKind Kind, List<AccessPathElement> Path, List<ILInstruction> Values, ILVariable Target) GetAccessPath(ILInstruction instruction, IType rootType)
{ {
List<AccessPathElement> path = new List<AccessPathElement>(); List<AccessPathElement> path = new List<AccessPathElement>();
ILVariable target = null; ILVariable target = null;
@ -164,7 +174,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
case CallInstruction call: case CallInstruction call:
if (!(call is CallVirt || call is Call)) goto default; if (!(call is CallVirt || call is Call)) goto default;
method = call.Method; method = call.Method;
if (!IsMethodApplicable(method, call.Arguments)) goto default; if (!IsMethodApplicable(method, call.Arguments, rootType)) goto default;
instruction = call.Arguments[0]; instruction = call.Arguments[0];
if (values == null) { if (values == null) {
values = new List<ILInstruction>(call.Arguments.Skip(1)); values = new List<ILInstruction>(call.Arguments.Skip(1));
@ -219,7 +229,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return (kind, path, values, target); return (kind, path, values, target);
} }
static bool IsMethodApplicable(IMethod method, IList<ILInstruction> arguments) static bool IsMethodApplicable(IMethod method, IList<ILInstruction> arguments, IType rootType)
{ {
if (!method.IsExtensionMethod && method.IsStatic) if (!method.IsExtensionMethod && method.IsStatic)
return false; return false;
@ -227,7 +237,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true; return true;
if (!"Add".Equals(method.Name, StringComparison.Ordinal) || arguments.Count == 0) if (!"Add".Equals(method.Name, StringComparison.Ordinal) || arguments.Count == 0)
return false; return false;
var targetType = GetReturnTypeFromInstruction(arguments[0]); var targetType = GetReturnTypeFromInstruction(arguments[0]) ?? rootType;
if (targetType == null) if (targetType == null)
return false; return false;
return targetType.GetAllBaseTypes().Any(i => i.IsKnownType(KnownTypeCode.IEnumerable) || i.IsKnownType(KnownTypeCode.IEnumerableOfT)); return targetType.GetAllBaseTypes().Any(i => i.IsKnownType(KnownTypeCode.IEnumerable) || i.IsKnownType(KnownTypeCode.IEnumerableOfT));
@ -235,7 +245,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
static IType GetReturnTypeFromInstruction(ILInstruction instruction) static IType GetReturnTypeFromInstruction(ILInstruction instruction)
{ {
// this switch must match the one in GetAccessPath
switch (instruction) { switch (instruction) {
case CallInstruction call: case CallInstruction call:
if (!(call is CallVirt || call is Call)) goto default; if (!(call is CallVirt || call is Call)) goto default;
@ -248,10 +257,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (stobj.Target is LdFlda ldflda2) if (stobj.Target is LdFlda ldflda2)
return ldflda2.Field.ReturnType; return ldflda2.Field.ReturnType;
goto default; goto default;
case LdLoc ldloc:
return ldloc.Variable.Type;
case LdLoca ldloca:
return ldloca.Variable.Type;
default: default:
return null; return null;
} }

Loading…
Cancel
Save