|
|
@ -243,7 +243,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
|
|
|
|
|
|
|
|
while (blob.RemainingBytes > 0) |
|
|
|
while (blob.RemainingBytes > 0) |
|
|
|
{ |
|
|
|
{ |
|
|
|
block.Instructions.Add(StElemPtr(tempStore, blob.Offset, new LdcI4(blob.ReadByte()), elementType)); |
|
|
|
block.Instructions.Add(StElemPtr(tempStore, blob.Offset, ReadElement(ref blob, elementType), elementType)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
block.FinalInstruction = new LdLoc(tempStore); |
|
|
|
block.FinalInstruction = new LdLoc(tempStore); |
|
|
@ -271,13 +271,30 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private ILInstruction ReadElement(ref BlobReader blob, IType elementType) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
switch (elementType.GetSize()) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
case 1: |
|
|
|
|
|
|
|
return new LdcI4(blob.ReadByte()); |
|
|
|
|
|
|
|
case 2: |
|
|
|
|
|
|
|
return new LdcI4(blob.ReadInt16()); |
|
|
|
|
|
|
|
case 4: |
|
|
|
|
|
|
|
return new LdcI4(blob.ReadInt32()); |
|
|
|
|
|
|
|
case 8: |
|
|
|
|
|
|
|
return new LdcI8(blob.ReadInt64()); |
|
|
|
|
|
|
|
default: |
|
|
|
|
|
|
|
throw new NotSupportedException(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool HandleCpblkInitializer(Block block, int pos, ILVariable v, long length, out BlobReader blob, out IType elementType) |
|
|
|
bool HandleCpblkInitializer(Block block, int pos, ILVariable v, long length, out BlobReader blob, out IType elementType) |
|
|
|
{ |
|
|
|
{ |
|
|
|
blob = default; |
|
|
|
blob = default; |
|
|
|
elementType = null; |
|
|
|
elementType = null; |
|
|
|
if (!block.Instructions[pos].MatchCpblk(out var dest, out var src, out var size)) |
|
|
|
if (!block.Instructions[pos].MatchCpblk(out var dest, out var src, out var size)) |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
if (!dest.MatchLdLoc(v) || !src.MatchLdsFlda(out var field) || !size.MatchLdcI4((int)length)) |
|
|
|
if (!dest.MatchLdLoc(v) || !MatchGetStaticFieldAddress(src, out var field) || !size.MatchLdcI4((int)length)) |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
if (!(v.IsSingleDefinition && v.LoadCount == 2)) |
|
|
|
if (!(v.IsSingleDefinition && v.LoadCount == 2)) |
|
|
|
return false; |
|
|
|
return false; |
|
|
@ -303,9 +320,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
else if (value is NewObj { Arguments: { Count: 2 } } newObj |
|
|
|
else if (value is NewObj { Arguments: { Count: 2 } } newObj |
|
|
|
&& newObj.Method.DeclaringType.IsKnownType(KnownTypeCode.SpanOfT) |
|
|
|
&& newObj.Method.DeclaringType.IsKnownType(KnownTypeCode.SpanOfT) |
|
|
|
&& newObj.Arguments[0].MatchLdLoc(v) |
|
|
|
&& newObj.Arguments[0].MatchLdLoc(v) |
|
|
|
&& newObj.Arguments[1].MatchLdcI4((int)length)) |
|
|
|
&& newObj.Arguments[1].MatchLdcI4(out var elementCount)) |
|
|
|
{ |
|
|
|
{ |
|
|
|
elementType = ((ParameterizedType)newObj.Method.DeclaringType).TypeArguments[0]; |
|
|
|
elementType = ((ParameterizedType)newObj.Method.DeclaringType).TypeArguments[0]; |
|
|
|
|
|
|
|
if (elementCount != length / elementType.GetSize()) |
|
|
|
|
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
else |
|
|
|
{ |
|
|
|
{ |
|
|
@ -315,7 +334,32 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
bool HandleSequentialLocAllocInitializer(Block block, int pos, ILVariable store, ILInstruction locAllocInstruction, out IType elementType, out StObj[] values, out int instructionsToRemove) |
|
|
|
bool MatchGetStaticFieldAddress(ILInstruction input, out IField field) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (input.MatchLdsFlda(out field)) |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
// call get_Item(addressof System.ReadOnlySpan`1[[T]](call CreateSpan(ldmembertoken field)), ldc.i4 0)
|
|
|
|
|
|
|
|
if (input is not Call { Method.Name: "get_Item", Arguments.Count: 2 } call) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
if (!call.Method.DeclaringType.IsKnownType(KnownTypeCode.ReadOnlySpanOfT)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
if (!call.Arguments[1].MatchLdcI4(0)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
if (call.Arguments[0] is not AddressOf addressOf) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
if (addressOf.Value is not Call { Method.Name: "CreateSpan", Arguments.Count: 1 } createSpanCall) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
if (!IsRuntimeHelpers(createSpanCall.Method.DeclaringType)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
if (!createSpanCall.Arguments[0].MatchLdMemberToken(out var member)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
field = member as IField; |
|
|
|
|
|
|
|
return field != null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool IsRuntimeHelpers(IType type) => type is { Name: "RuntimeHelpers", Namespace: "System.Runtime.CompilerServices" }; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
unsafe bool HandleSequentialLocAllocInitializer(Block block, int pos, ILVariable store, ILInstruction locAllocInstruction, out IType elementType, out StObj[] values, out int instructionsToRemove) |
|
|
|
{ |
|
|
|
{ |
|
|
|
int elementCount = 0; |
|
|
|
int elementCount = 0; |
|
|
|
long minExpectedOffset = 0; |
|
|
|
long minExpectedOffset = 0; |
|
|
@ -326,24 +370,60 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
if (!locAllocInstruction.MatchLocAlloc(out var lengthInstruction)) |
|
|
|
if (!locAllocInstruction.MatchLocAlloc(out var lengthInstruction)) |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
|
|
if (block.Instructions[pos].MatchInitblk(out var dest, out var value, out var size) |
|
|
|
BlobReader blob = default; |
|
|
|
&& lengthInstruction.MatchLdcI(out long byteCount)) |
|
|
|
|
|
|
|
|
|
|
|
if (lengthInstruction.MatchLdcI(out long byteCount)) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (!dest.MatchLdLoc(store) || !size.MatchLdcI(byteCount)) |
|
|
|
if (block.Instructions[pos].MatchInitblk(out var dest, out var value, out var size)) |
|
|
|
return false; |
|
|
|
{ |
|
|
|
instructionsToRemove++; |
|
|
|
if (!dest.MatchLdLoc(store) || !size.MatchLdcI(byteCount)) |
|
|
|
pos++; |
|
|
|
return false; |
|
|
|
|
|
|
|
instructionsToRemove++; |
|
|
|
|
|
|
|
pos++; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else if (block.Instructions[pos].MatchCpblk(out dest, out var src, out size)) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (!dest.MatchLdLoc(store) || !size.MatchLdcI(byteCount)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
if (!MatchGetStaticFieldAddress(src, out var field)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
var fd = context.PEFile.Metadata.GetFieldDefinition((FieldDefinitionHandle)field.MetadataToken); |
|
|
|
|
|
|
|
if (!fd.HasFlag(System.Reflection.FieldAttributes.HasFieldRVA)) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
blob = fd.GetInitialValue(context.PEFile, context.TypeSystem); |
|
|
|
|
|
|
|
instructionsToRemove++; |
|
|
|
|
|
|
|
pos++; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for (int i = pos; i < block.Instructions.Count; i++) |
|
|
|
for (int i = pos; i < block.Instructions.Count; i++) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// match the basic stobj pattern
|
|
|
|
// match the basic stobj pattern
|
|
|
|
if (!block.Instructions[i].MatchStObj(out ILInstruction target, out value, out var currentType) |
|
|
|
if (!block.Instructions[i].MatchStObj(out ILInstruction target, out var value, out var currentType) |
|
|
|
|| value.Descendants.OfType<IInstructionWithVariableOperand>().Any(inst => inst.Variable == store)) |
|
|
|
|| value.Descendants.OfType<IInstructionWithVariableOperand>().Any(inst => inst.Variable == store)) |
|
|
|
break; |
|
|
|
break; |
|
|
|
if (elementType != null && !currentType.Equals(elementType)) |
|
|
|
// first
|
|
|
|
|
|
|
|
if (elementType == null) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
elementType = currentType; |
|
|
|
|
|
|
|
if (blob.StartPointer != null) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
var countInstruction = PointerArithmeticOffset.Detect(lengthInstruction, elementType, checkForOverflow: true); |
|
|
|
|
|
|
|
if (countInstruction == null || !countInstruction.MatchLdcI(out long valuesLength) || valuesLength < 1) |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
values = new StObj[(int)valuesLength]; |
|
|
|
|
|
|
|
int valueIndex = 0; |
|
|
|
|
|
|
|
while (blob.RemainingBytes > 0 && valueIndex < values.Length) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
values[valueIndex] = StElemPtr(store, blob.Offset, ReadElement(ref blob, elementType), elementType); |
|
|
|
|
|
|
|
valueIndex++; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else if (!currentType.Equals(elementType)) |
|
|
|
|
|
|
|
{ |
|
|
|
break; |
|
|
|
break; |
|
|
|
elementType = currentType; |
|
|
|
} |
|
|
|
// match the target
|
|
|
|
// match the target
|
|
|
|
// should be either ldloc store (at offset 0)
|
|
|
|
// should be either ldloc store (at offset 0)
|
|
|
|
// or binary.add(ldloc store, offset) where offset is either 'elementSize' or 'i * elementSize'
|
|
|
|
// or binary.add(ldloc store, offset) where offset is either 'elementSize' or 'i * elementSize'
|
|
|
@ -403,7 +483,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
return new StObj(targetInst, storeInstruction.Value, storeInstruction.Type); |
|
|
|
return new StObj(targetInst, storeInstruction.Value, storeInstruction.Type); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
ILInstruction StElemPtr(ILVariable target, int offset, LdcI4 value, IType type) |
|
|
|
StObj StElemPtr(ILVariable target, int offset, ILInstruction value, IType type) |
|
|
|
{ |
|
|
|
{ |
|
|
|
var targetInst = offset == 0 ? (ILInstruction)new LdLoc(target) : new BinaryNumericInstruction( |
|
|
|
var targetInst = offset == 0 ? (ILInstruction)new LdLoc(target) : new BinaryNumericInstruction( |
|
|
|
BinaryNumericOperator.Add, |
|
|
|
BinaryNumericOperator.Add, |
|
|
@ -698,12 +778,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms |
|
|
|
IMethod method = call.Method; |
|
|
|
IMethod method = call.Method; |
|
|
|
if (!method.IsStatic || method.Name != "InitializeArray" || method.DeclaringTypeDefinition == null) |
|
|
|
if (!method.IsStatic || method.Name != "InitializeArray" || method.DeclaringTypeDefinition == null) |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
var declaringType = method.DeclaringTypeDefinition; |
|
|
|
if (!IsRuntimeHelpers(method.DeclaringType)) |
|
|
|
if (declaringType.DeclaringType != null || declaringType.Name != "RuntimeHelpers" |
|
|
|
|
|
|
|
|| declaringType.Namespace != "System.Runtime.CompilerServices") |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
array = call.Arguments[0]; |
|
|
|
array = call.Arguments[0]; |
|
|
|
if (!call.Arguments[1].MatchLdMemberToken(out var member)) |
|
|
|
if (!call.Arguments[1].MatchLdMemberToken(out var member)) |
|
|
|
return false; |
|
|
|
return false; |
|
|
|