diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index 8091a848d..896b79d05 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -300,8 +300,9 @@ namespace ICSharpCode.Decompiler.Ast if (type is Mono.Cecil.ByReferenceType) { typeIndex++; - // ignore by reference type (cannot be represented in C#) - return ConvertType((type as Mono.Cecil.ByReferenceType).ElementType, typeAttributes, ref typeIndex); + // by reference type cannot be represented in C#; so we'll represent it as a pointer instead + return ConvertType((type as Mono.Cecil.ByReferenceType).ElementType, typeAttributes, ref typeIndex) + .MakePointerType(); } else if (type is Mono.Cecil.PointerType) { typeIndex++; return ConvertType((type as Mono.Cecil.PointerType).ElementType, typeAttributes, ref typeIndex) @@ -850,6 +851,9 @@ namespace ICSharpCode.Decompiler.Ast if (paramDef.ParameterType is ByReferenceType) { astParam.ParameterModifier = (!paramDef.IsIn && paramDef.IsOut) ? ParameterModifier.Out : ParameterModifier.Ref; + ComposedType ct = astParam.Type as ComposedType; + if (ct != null && ct.PointerRank > 0) + ct.PointerRank--; } if (paramDef.HasCustomAttributes) { foreach (CustomAttribute ca in paramDef.CustomAttributes) { diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 845c83818..e0aefd41f 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -181,6 +181,21 @@ namespace ICSharpCode.Decompiler.Ast tryCatchStmt.CatchClauses.Add(cc); } yield return tryCatchStmt; + } else if (node is ILFixedStatement) { + ILFixedStatement fixedNode = (ILFixedStatement)node; + ILVariable v; + ILExpression init; + if (!fixedNode.Initializer.Match(ILCode.Stloc, out v, out init)) + throw new InvalidOperationException("Fixed initializer must be an assignment to a local variable"); + FixedStatement fixedStatement = new FixedStatement(); + fixedStatement.Type = AstBuilder.ConvertType(v.Type); + fixedStatement.Variables.Add( + new VariableInitializer { + Name = v.Name, + Initializer = (Expression)TransformExpression(init) + }.WithAnnotation(v)); + fixedStatement.EmbeddedStatement = TransformBlock(fixedNode.BodyBlock); + yield return fixedStatement; } else if (node is ILBlock) { yield return TransformBlock((ILBlock)node); } else if (node is ILComment) { diff --git a/ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs b/ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs index 36d319fa8..6f38542f6 100644 --- a/ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs +++ b/ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs @@ -54,6 +54,10 @@ namespace ICSharpCode.Decompiler.Ast if (ident != null && ident.Identifier == name && ident.TypeArguments.Count == 0) return node; + FixedStatement fixedStatement = node as FixedStatement; + if (fixedStatement != null && fixedStatement.Variables.Single().Name == name) + return null; // no need to introduce the variable here + AstNode withinCurrent = FindInsertPos(node.FirstChild, name, allowPassIntoLoops); if (withinCurrent != null) { if (pos == null) { diff --git a/ICSharpCode.Decompiler/Ast/NameVariables.cs b/ICSharpCode.Decompiler/Ast/NameVariables.cs index aabeec440..8feaf16f0 100644 --- a/ICSharpCode.Decompiler/Ast/NameVariables.cs +++ b/ICSharpCode.Decompiler/Ast/NameVariables.cs @@ -216,7 +216,7 @@ namespace ICSharpCode.Decompiler.Ast string name; if (type.IsArray) { name = "array"; - } else if (type.IsPointer) { + } else if (type.IsPointer || type.IsByReference) { name = "ptr"; } else if (!typeNameToVariableNameDict.TryGetValue(type.FullName, out name)) { name = type.Name; diff --git a/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs b/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs index 936313b72..fd716eb00 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs @@ -28,6 +28,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms public override bool VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression, object data) { + base.VisitPointerReferenceExpression(pointerReferenceExpression, data); return true; } @@ -41,10 +42,34 @@ namespace ICSharpCode.Decompiler.Ast.Transforms public override bool VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, object data) { - if (unaryOperatorExpression.Operator == UnaryOperatorType.Dereference || unaryOperatorExpression.Operator == UnaryOperatorType.AddressOf) + base.VisitUnaryOperatorExpression(unaryOperatorExpression, data); + if (unaryOperatorExpression.Operator == UnaryOperatorType.Dereference) { + BinaryOperatorExpression bop = unaryOperatorExpression.Expression as BinaryOperatorExpression; + if (bop != null && bop.Operator == BinaryOperatorType.Add) { + // TODO: transform "*(ptr + int)" to "ptr[int]" + } return true; - else - return base.VisitUnaryOperatorExpression(unaryOperatorExpression, data); + } else if (unaryOperatorExpression.Operator == UnaryOperatorType.AddressOf) { + return true; + } else { + return false; + } + } + + public override bool VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression, object data) + { + bool result = base.VisitMemberReferenceExpression(memberReferenceExpression, data); + UnaryOperatorExpression uoe = memberReferenceExpression.Target as UnaryOperatorExpression; + if (uoe != null && uoe.Operator == UnaryOperatorType.Dereference) { + PointerReferenceExpression pre = new PointerReferenceExpression(); + pre.Target = uoe.Expression.Detach(); + pre.MemberName = memberReferenceExpression.MemberName; + memberReferenceExpression.TypeArguments.MoveTo(pre.TypeArguments); + pre.CopyAnnotationsFrom(uoe); + pre.CopyAnnotationsFrom(memberReferenceExpression); + memberReferenceExpression.ReplaceWith(pre); + } + return result; } } } diff --git a/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs b/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs index 7cbbb7d3b..1b4f680d6 100644 --- a/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs +++ b/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs @@ -118,7 +118,7 @@ namespace ICSharpCode.Decompiler.Disassembler { if (type is PinnedType) { writer.Write("pinned "); - type.GetElementType().WriteTo(writer, onlyName, shortName); + ((PinnedType)type).ElementType.WriteTo(writer, onlyName, shortName); } else if (type is ArrayType) { ArrayType at = (ArrayType)type; at.ElementType.WriteTo(writer, onlyName, shortName); @@ -128,10 +128,10 @@ namespace ICSharpCode.Decompiler.Disassembler } else if (type is GenericParameter) { writer.WriteReference(type.Name, type); } else if (type is ByReferenceType) { - type.GetElementType().WriteTo(writer, onlyName, shortName); + ((ByReferenceType)type).ElementType.WriteTo(writer, onlyName, shortName); writer.Write('&'); } else if (type is PointerType) { - type.GetElementType().WriteTo(writer, onlyName, shortName); + ((PointerType)type).ElementType.WriteTo(writer, onlyName, shortName); writer.Write('*'); } else if (type is GenericInstanceType) { type.GetElementType().WriteTo(writer, onlyName, shortName); @@ -147,12 +147,12 @@ namespace ICSharpCode.Decompiler.Disassembler writer.Write("modopt("); ((OptionalModifierType)type).ModifierType.WriteTo(writer, true, shortName); writer.Write(") "); - type.GetElementType().WriteTo(writer, onlyName, shortName); + ((OptionalModifierType)type).ElementType.WriteTo(writer, onlyName, shortName); } else if (type is RequiredModifierType) { writer.Write("modreq("); ((RequiredModifierType)type).ModifierType.WriteTo(writer, true, shortName); writer.Write(") "); - type.GetElementType().WriteTo(writer, onlyName, shortName); + ((RequiredModifierType)type).ElementType.WriteTo(writer, onlyName, shortName); } else { string name = PrimitiveTypeName(type); if (name != null) { diff --git a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs index f345ca5a8..397faf050 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs @@ -486,15 +486,17 @@ namespace ICSharpCode.Decompiler.ILAst TypeReference varType = methodDef.Body.Variables[variableIndex].VariableType; List newVars; - + + bool isPinned = methodDef.Body.Variables[variableIndex].IsPinned; + // If the variable is pinned, use single variable. // If any of the loads is from "all", use single variable // If any of the loads is ldloca, fallback to single variable as well - if (loads.Any(b => b.VariablesBefore[variableIndex].StoredByAll || b.Code == ILCode.Ldloca)) { + if (isPinned || loads.Any(b => b.VariablesBefore[variableIndex].StoredByAll || b.Code == ILCode.Ldloca)) { newVars = new List(1) { new VariableInfo() { Variable = new ILVariable() { Name = "var_" + variableIndex, - Type = varType, - OriginalVariable = methodDef.Body.Variables[variableIndex] + Type = isPinned ? ((PinnedType)varType).ElementType : varType, + OriginalVariable = methodDef.Body.Variables[variableIndex] }, Stores = stores, Loads = loads diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 45d7731c0..609c5f763 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -125,10 +125,7 @@ namespace ICSharpCode.Decompiler.ILAst // open up additional inlining possibilities. new ILInlining(method).InlineAllVariables(); - foreach (ILExpression expr in method.GetSelfAndChildrenRecursive()) { - expr.InferredType = null; - expr.ExpectedType = null; - } + TypeAnalysis.Reset(method); if (abortBeforeStep == ILAstOptimizationStep.PeepholeTransforms) return; PeepholeTransforms.Run(context, method); @@ -699,8 +696,10 @@ namespace ICSharpCode.Decompiler.ILAst { switch(expr.Code) { case ILCode.Call: - case ILCode.Calli: case ILCode.Callvirt: + // property getters can't be expression statements, but all other method calls can be + MethodReference mr = (MethodReference)expr.Operand; + return !mr.Name.StartsWith("get_", StringComparison.Ordinal); case ILCode.Newobj: case ILCode.Newarr: return true; diff --git a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs index 3608cacc1..dfafb45bf 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs @@ -202,6 +202,10 @@ namespace ICSharpCode.Decompiler.ILAst public VariableDefinition OriginalVariable; public ParameterDefinition OriginalParameter; + public bool IsPinned { + get { return OriginalVariable != null && OriginalVariable.IsPinned; } + } + public bool IsParameter { get { return OriginalParameter != null; } } @@ -346,6 +350,11 @@ namespace ICSharpCode.Decompiler.ILAst if (this.InferredType != null) { output.Write(':'); this.InferredType.WriteTo(output, true, true); + if (this.ExpectedType != null && this.ExpectedType.FullName != this.InferredType.FullName) { + output.Write("[exp:"); + this.ExpectedType.WriteTo(output, true, true); + output.Write(']'); + } } return; } @@ -516,4 +525,30 @@ namespace ICSharpCode.Decompiler.ILAst output.WriteLine("}"); } } + + public class ILFixedStatement : ILNode + { + public ILExpression Initializer; + public ILBlock BodyBlock; + + public override IEnumerable GetChildren() + { + if (this.Initializer != null) + yield return this.Initializer; + if (this.BodyBlock != null) + yield return this.BodyBlock; + } + + public override void WriteTo(ITextOutput output) + { + output.Write("fixed ("); + if (this.Initializer != null) + this.Initializer.WriteTo(output); + output.WriteLine(") {"); + output.Indent(); + this.BodyBlock.WriteTo(output); + output.Unindent(); + output.WriteLine("}"); + } + } } \ No newline at end of file diff --git a/ICSharpCode.Decompiler/ILAst/ILCodes.cs b/ICSharpCode.Decompiler/ILAst/ILCodes.cs index 7d5daed98..ea0569582 100644 --- a/ICSharpCode.Decompiler/ILAst/ILCodes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILCodes.cs @@ -433,6 +433,8 @@ namespace ICSharpCode.Decompiler.ILAst case ILCode.__Stind_I2: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.Int16; break; case ILCode.__Stind_I4: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.Int32; break; case ILCode.__Stind_I8: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.Int64; break; + case ILCode.__Stind_R4: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.Single; break; + case ILCode.__Stind_R8: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.Double; break; } } diff --git a/ICSharpCode.Decompiler/ILAst/ILInlining.cs b/ICSharpCode.Decompiler/ILAst/ILInlining.cs index b2de137fa..b79287bd6 100644 --- a/ICSharpCode.Decompiler/ILAst/ILInlining.cs +++ b/ICSharpCode.Decompiler/ILAst/ILInlining.cs @@ -113,7 +113,7 @@ namespace ICSharpCode.Decompiler.ILAst { ILVariable v; ILExpression inlinedExpression; - if (block.Body[pos].Match(ILCode.Stloc, out v, out inlinedExpression)) { + if (block.Body[pos].Match(ILCode.Stloc, out v, out inlinedExpression) && !v.IsPinned) { if (InlineIfPossible(v, inlinedExpression, block.Body.ElementAtOrDefault(pos+1), aggressive)) { // Assign the ranges of the stloc instruction: inlinedExpression.ILRanges.AddRange(((ILExpression)block.Body[pos]).ILRanges); diff --git a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs index 6d21cd782..8fbd47c4d 100644 --- a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs +++ b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs @@ -30,7 +30,8 @@ namespace ICSharpCode.Decompiler.ILAst initializerTransforms.TransformArrayInitializers, initializerTransforms.TransformCollectionInitializers, transforms.CachedDelegateInitialization, - transforms.MakeAssignmentExpression + transforms.MakeAssignmentExpression, + transforms.IntroduceFixedStatements }; Func[] exprTransforms = { HandleDecimalConstants, @@ -292,5 +293,105 @@ namespace ICSharpCode.Decompiler.ILAst return false; } #endregion + + #region IntroduceFixedStatements + void IntroduceFixedStatements(ILBlock block, ref int i) + { + // stloc(pinned_Var, conv.u(ldc.i4(0))) + ILExpression initValue; + ILVariable pinnedVar; + if (!MatchFixedInitializer(block, i, out pinnedVar, out initValue)) + return; + // find initialization of v: + int j; + for (j = i + 1; j < block.Body.Count; j++) { + ILVariable v2; + ILExpression storedVal; + if (block.Body[j].Match(ILCode.Stloc, out v2, out storedVal) && v2 == pinnedVar) { + if (IsNullOrZero(storedVal)) { + // Create fixed statement from i to j + ILFixedStatement stmt = new ILFixedStatement(); + stmt.Initializer = initValue; + stmt.BodyBlock = new ILBlock(block.Body.GetRange(i + 1, j - i - 1)); // from i+1 to j-1 (inclusive) + block.Body.RemoveRange(i + 1, j - i); // from j+1 to i (inclusive) + block.Body[i] = stmt; + if (pinnedVar.Type.IsByReference) + pinnedVar.Type = new PointerType(((ByReferenceType)pinnedVar.Type).ElementType); + break; + } + } + } + } + + bool IsNullOrZero(ILExpression expr) + { + if (expr.Code == ILCode.Conv_U || expr.Code == ILCode.Conv_I) + expr = expr.Arguments[0]; + return (expr.Code == ILCode.Ldc_I4 && (int)expr.Operand == 0) || expr.Code == ILCode.Ldnull; + } + + bool MatchFixedInitializer(ILBlock block, int i, out ILVariable pinnedVar, out ILExpression initValue) + { + if (block.Body[i].Match(ILCode.Stloc, out pinnedVar, out initValue)) { + initValue = (ILExpression)block.Body[i]; + return pinnedVar.IsPinned; + } + ILCondition ifStmt = block.Body[i] as ILCondition; + ILExpression arrayLoadingExpr; + if (ifStmt != null && MatchFixedArrayInitializerCondition(ifStmt.Condition, out arrayLoadingExpr)) { + ILVariable arrayVariable = (ILVariable)arrayLoadingExpr.Operand; + ILExpression trueValue; + if (ifStmt.TrueBlock != null && ifStmt.TrueBlock.Body.Count == 1 + && ifStmt.TrueBlock.Body[0].Match(ILCode.Stloc, out pinnedVar, out trueValue) + && pinnedVar.IsPinned && IsNullOrZero(trueValue)) + { + ILVariable stlocVar; + ILExpression falseValue; + if (ifStmt.FalseBlock != null && ifStmt.FalseBlock.Body.Count == 1 + && ifStmt.FalseBlock.Body[0].Match(ILCode.Stloc, out stlocVar, out falseValue) && stlocVar == pinnedVar) + { + ILVariable loadedVariable; + if (falseValue.Code == ILCode.Ldelema + && falseValue.Arguments[0].Match(ILCode.Ldloc, out loadedVariable) && loadedVariable == arrayVariable + && IsNullOrZero(falseValue.Arguments[1])) + { + initValue = new ILExpression(ILCode.Stloc, pinnedVar, arrayLoadingExpr); + return true; + } + } + } + } + initValue = null; + return false; + } + + bool MatchFixedArrayInitializerCondition(ILExpression condition, out ILExpression initValue) + { + ILExpression logicAnd; + ILVariable arrayVar1, arrayVar2; + if (condition.Match(ILCode.LogicNot, out logicAnd) && logicAnd.Code == ILCode.LogicAnd) { + initValue = UnpackDoubleNegation(logicAnd.Arguments[0]); + if (initValue.Match(ILCode.Ldloc, out arrayVar1)) { + ILExpression arrayLength = logicAnd.Arguments[1]; + if (arrayLength.Code == ILCode.Conv_I4) + arrayLength = arrayLength.Arguments[0]; + if (arrayLength.Code == ILCode.Ldlen && arrayLength.Arguments[0].Match(ILCode.Ldloc, out arrayVar2)) { + return arrayVar1 == arrayVar2; + } + } + } + initValue = null; + return false; + } + + ILExpression UnpackDoubleNegation(ILExpression expr) + { + ILExpression negated; + if (expr.Match(ILCode.LogicNot, out negated) && negated.Match(ILCode.LogicNot, out negated)) + return negated; + else + return expr; + } + #endregion } } diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs index 2b0947484..397e5e485 100644 --- a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -45,24 +45,32 @@ namespace ICSharpCode.Decompiler.ILAst { ILCondition cond = node as ILCondition; if (cond != null) { - InferTypeForExpression(cond.Condition, typeSystem.Boolean, false); + cond.Condition.ExpectedType = typeSystem.Boolean; } ILWhileLoop loop = node as ILWhileLoop; if (loop != null && loop.Condition != null) { - InferTypeForExpression(loop.Condition, typeSystem.Boolean, false); + loop.Condition.ExpectedType = typeSystem.Boolean; } ILExpression expr = node as ILExpression; if (expr != null) { - ILVariable v = expr.Operand as ILVariable; - if (v != null && v.IsGenerated && v.Type == null && expr.Code == ILCode.Stloc && !inferredVariables.Contains(v) && HasSingleLoad(v)) { - // Don't deal with this node or its children yet, - // wait for the expected type to be inferred first. - // This happens with the arg_... variables introduced by the ILAst - we skip inferring the whole statement, - // and first infer the statement that reads from the arg_... variable. - // The ldloc inference will write the expected type to the variable, and the next InferRemainingStores() pass - // will then infer this statement with the correct expected type. - storedToGeneratedVariables.Add(expr); - return; + foreach (ILExpression store in expr.GetSelfAndChildrenRecursive(e => e.Code == ILCode.Stloc)) { + ILVariable v = (ILVariable)store.Operand; + if (v.IsGenerated && v.Type == null && !inferredVariables.Contains(v) && HasSingleLoad(v)) { + // Don't deal with this node or its children yet, + // wait for the expected type to be inferred first. + // This happens with the arg_... variables introduced by the ILAst - we skip inferring the whole statement, + // and first infer the statement that reads from the arg_... variable. + // The ldloc inference will write the expected type to the variable, and the next InferRemainingStores() pass + // will then infer this statement with the correct expected type. + storedToGeneratedVariables.Add(expr); + // However, it is possible that this statement both writes to and reads from the variable (think inlined assignments). + if (expr.GetSelfAndChildrenRecursive(e => e.Code == ILCode.Ldlen && e.Operand == v).Any()) { + // In this case, we analyze it now anyways, and will re-evaluate it later + break; + } else { + return; + } + } } bool anyArgumentIsMissingType = expr.Arguments.Any(a => a.InferredType == null); if (expr.InferredType == null || anyArgumentIsMissingType) @@ -184,6 +192,8 @@ namespace ICSharpCode.Decompiler.ILAst Instruction constraint = expr.GetPrefix(Code.Constrained); if (constraint != null) InferTypeForExpression(expr.Arguments[i], new ByReferenceType((TypeReference)constraint.Operand)); + else if (method.DeclaringType.IsValueType) + InferTypeForExpression(expr.Arguments[i], new ByReferenceType(method.DeclaringType)); else InferTypeForExpression(expr.Arguments[i], method.DeclaringType); } else { @@ -247,18 +257,54 @@ namespace ICSharpCode.Decompiler.ILAst } return null; case ILCode.Ldobj: - if (forceInferChildren) { - if (InferTypeForExpression(expr.Arguments[0], new ByReferenceType((TypeReference)expr.Operand)) is PointerType) - InferTypeForExpression(expr.Arguments[0], new PointerType((TypeReference)expr.Operand)); + { + TypeReference type = (TypeReference)expr.Operand; + if (expectedType != null) { + int infoAmount = GetInformationAmount(expectedType); + if (infoAmount == 1 && GetInformationAmount(type) == 8) { + // A bool can be loaded from both bytes and sbytes. + type = expectedType; + } + if (infoAmount >= 8 && infoAmount <= 64 && infoAmount == GetInformationAmount(type)) { + // An integer can be loaded as another integer of the same size. + // For integers smaller than 32 bit, the signs must match (as loading performs sign extension) + if (infoAmount >= 32 || IsSigned(expectedType) == IsSigned(type)) + type = expectedType; + } + } + if (forceInferChildren) { + if (InferTypeForExpression(expr.Arguments[0], new ByReferenceType(type)) is PointerType) + InferTypeForExpression(expr.Arguments[0], new PointerType(type)); + } + return type; } - return (TypeReference)expr.Operand; case ILCode.Stobj: - if (forceInferChildren) { - if (InferTypeForExpression(expr.Arguments[0], new ByReferenceType((TypeReference)expr.Operand)) is PointerType) - InferTypeForExpression(expr.Arguments[0], new PointerType((TypeReference)expr.Operand)); - InferTypeForExpression(expr.Arguments[1], (TypeReference)expr.Operand); + { + TypeReference operandType = (TypeReference)expr.Operand; + TypeReference pointerType = InferTypeForExpression(expr.Arguments[0], new ByReferenceType(operandType)); + TypeReference elementType; + if (pointerType is PointerType) + elementType = ((PointerType)pointerType).ElementType; + else if (pointerType is ByReferenceType) + elementType = ((ByReferenceType)pointerType).ElementType; + else + elementType = null; + if (elementType != null) { + // An integer can be stored in any other integer of the same size. + int infoAmount = GetInformationAmount(elementType); + if (infoAmount == 1) infoAmount = 8; + if (infoAmount == GetInformationAmount(operandType) && IsSigned(elementType) != null && IsSigned(operandType) != null) + operandType = elementType; + } + if (forceInferChildren) { + if (pointerType is PointerType) + InferTypeForExpression(expr.Arguments[0], new PointerType(operandType)); + else if (operandType != expr.Operand) + InferTypeForExpression(expr.Arguments[0], new ByReferenceType(operandType)); + InferTypeForExpression(expr.Arguments[1], operandType); + } + return operandType; } - return (TypeReference)expr.Operand; case ILCode.Initobj: return null; case ILCode.DefaultValue: @@ -284,19 +330,19 @@ namespace ICSharpCode.Decompiler.ILAst case ILCode.Or: case ILCode.And: case ILCode.Xor: - return InferArgumentsInBinaryOperator(expr, null); + return InferArgumentsInBinaryOperator(expr, null, expectedType); case ILCode.Add_Ovf: case ILCode.Sub_Ovf: case ILCode.Mul_Ovf: case ILCode.Div: case ILCode.Rem: - return InferArgumentsInBinaryOperator(expr, true); + return InferArgumentsInBinaryOperator(expr, true, expectedType); case ILCode.Add_Ovf_Un: case ILCode.Sub_Ovf_Un: case ILCode.Mul_Ovf_Un: case ILCode.Div_Un: case ILCode.Rem_Un: - return InferArgumentsInBinaryOperator(expr, false); + return InferArgumentsInBinaryOperator(expr, false, expectedType); case ILCode.Shl: case ILCode.Shr: if (forceInferChildren) @@ -403,31 +449,31 @@ namespace ICSharpCode.Decompiler.ILAst case ILCode.Conv_I1: case ILCode.Conv_Ovf_I1: case ILCode.Conv_Ovf_I1_Un: - return HandleConversion(8, true, expr.Arguments[0], expectedType, typeSystem.Byte); + return HandleConversion(8, true, expr.Arguments[0], expectedType, typeSystem.SByte); case ILCode.Conv_I2: case ILCode.Conv_Ovf_I2: case ILCode.Conv_Ovf_I2_Un: - return HandleConversion(16, true, expr.Arguments[0], expectedType, typeSystem.UInt16); + return HandleConversion(16, true, expr.Arguments[0], expectedType, typeSystem.Int16); case ILCode.Conv_I4: case ILCode.Conv_Ovf_I4: case ILCode.Conv_Ovf_I4_Un: - return HandleConversion(32, true, expr.Arguments[0], expectedType, typeSystem.UInt32); + return HandleConversion(32, true, expr.Arguments[0], expectedType, typeSystem.Int32); case ILCode.Conv_I8: case ILCode.Conv_Ovf_I8: case ILCode.Conv_Ovf_I8_Un: - return HandleConversion(64, true, expr.Arguments[0], expectedType, typeSystem.UInt64); + return HandleConversion(64, true, expr.Arguments[0], expectedType, typeSystem.Int64); case ILCode.Conv_U1: case ILCode.Conv_Ovf_U1: case ILCode.Conv_Ovf_U1_Un: - return HandleConversion(8, false, expr.Arguments[0], expectedType, typeSystem.SByte); + return HandleConversion(8, false, expr.Arguments[0], expectedType, typeSystem.Byte); case ILCode.Conv_U2: case ILCode.Conv_Ovf_U2: case ILCode.Conv_Ovf_U2_Un: - return HandleConversion(16, false, expr.Arguments[0], expectedType, typeSystem.Int16); + return HandleConversion(16, false, expr.Arguments[0], expectedType, typeSystem.UInt16); case ILCode.Conv_U4: case ILCode.Conv_Ovf_U4: case ILCode.Conv_Ovf_U4_Un: - return HandleConversion(32, false, expr.Arguments[0], expectedType, typeSystem.Int32); + return HandleConversion(32, false, expr.Arguments[0], expectedType, typeSystem.UInt32); case ILCode.Conv_U8: case ILCode.Conv_Ovf_U8: case ILCode.Conv_Ovf_U8_Un: @@ -458,17 +504,17 @@ namespace ICSharpCode.Decompiler.ILAst #region Comparison instructions case ILCode.Ceq: if (forceInferChildren) - InferArgumentsInBinaryOperator(expr, null); + InferArgumentsInBinaryOperator(expr, null, null); return typeSystem.Boolean; case ILCode.Clt: case ILCode.Cgt: if (forceInferChildren) - InferArgumentsInBinaryOperator(expr, true); + InferArgumentsInBinaryOperator(expr, true, null); return typeSystem.Boolean; case ILCode.Clt_Un: case ILCode.Cgt_Un: if (forceInferChildren) - InferArgumentsInBinaryOperator(expr, false); + InferArgumentsInBinaryOperator(expr, false, null); return typeSystem.Boolean; #endregion #region Branch instructions @@ -613,12 +659,12 @@ namespace ICSharpCode.Decompiler.ILAst return type; } - TypeReference InferArgumentsInBinaryOperator(ILExpression expr, bool? isSigned) + TypeReference InferArgumentsInBinaryOperator(ILExpression expr, bool? isSigned, TypeReference expectedType) { ILExpression left = expr.Arguments[0]; ILExpression right = expr.Arguments[1]; - TypeReference leftPreferred = DoInferTypeForExpression(left, null); - TypeReference rightPreferred = DoInferTypeForExpression(right, null); + TypeReference leftPreferred = DoInferTypeForExpression(left, expectedType); + TypeReference rightPreferred = DoInferTypeForExpression(right, expectedType); if (leftPreferred == rightPreferred) { return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = leftPreferred; } else if (rightPreferred == DoInferTypeForExpression(left, rightPreferred)) { @@ -768,5 +814,19 @@ namespace ICSharpCode.Decompiler.ILAst return TypeCode.Object; } } + + /// + /// Clears the type inference data on the method. + /// + public static void Reset(ILBlock method) + { + foreach (ILExpression expr in method.GetSelfAndChildrenRecursive()) { + expr.InferredType = null; + expr.ExpectedType = null; + ILVariable v = expr.Operand as ILVariable; + if (v != null && v.IsGenerated) + v.Type = null; + } + } } } diff --git a/ICSharpCode.Decompiler/Tests/UnsafeCode.cs b/ICSharpCode.Decompiler/Tests/UnsafeCode.cs index e44bdc3ac..9fc546161 100644 --- a/ICSharpCode.Decompiler/Tests/UnsafeCode.cs +++ b/ICSharpCode.Decompiler/Tests/UnsafeCode.cs @@ -26,4 +26,34 @@ public class UnsafeCode { PassRefParameterAsPointer(ref *p); } + + public unsafe void FixedStringAccess(string text) + { + fixed (char* c = text) { + char* tmp = c; + while (*tmp != 0) { + *tmp = 'A'; + tmp++; + } + } + } + + public unsafe void PutDoubleIntoLongArray1(long[] array, int index, double val) + { + fixed (long* l = array) { + ((double*)l)[index] = val; + } + } + + public unsafe void PutDoubleIntoLongArray2(long[] array, int index, double val) + { + fixed (long* l = &array[index]) { + *(double*)l = val; + } + } + + public unsafe string PointerReferenceExpression(double* d) + { + return d->ToString(); + } } diff --git a/ILSpy/CSharpLanguage.cs b/ILSpy/CSharpLanguage.cs index 1234c90be..11bc1b3dc 100644 --- a/ILSpy/CSharpLanguage.cs +++ b/ILSpy/CSharpLanguage.cs @@ -408,6 +408,9 @@ namespace ICSharpCode.ILSpy w.Write("out "); else w.Write("ref "); + + if (astType is ComposedType && ((ComposedType)astType).PointerRank > 0) + ((ComposedType)astType).PointerRank--; } astType.AcceptVisitor(new OutputVisitor(w, new CSharpFormattingPolicy()), null); diff --git a/ILSpy/ILAstLanguage.cs b/ILSpy/ILAstLanguage.cs index 30a8716ce..a931fac4c 100644 --- a/ILSpy/ILAstLanguage.cs +++ b/ILSpy/ILAstLanguage.cs @@ -66,6 +66,8 @@ namespace ICSharpCode.ILSpy output.WriteDefinition(v.Name, v); if (v.Type != null) { output.Write(" : "); + if (v.IsPinned) + output.Write("pinned "); v.Type.WriteTo(output, true, true); } output.WriteLine(); diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/ComposedType.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/ComposedType.cs index ce8ad9879..b888cdea2 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/ComposedType.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/ComposedType.cs @@ -55,6 +55,8 @@ namespace ICSharpCode.NRefactory.CSharp return GetChildrenByRole(PointerRole).Count(); } set { + if (value < 0) + throw new ArgumentOutOfRangeException(); int d = this.PointerRank; while (d > value) { GetChildByRole(PointerRole).Remove(); diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs index f12d3ffac..da843f39c 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs @@ -1387,9 +1387,13 @@ namespace ICSharpCode.NRefactory.CSharp { StartNode(fixedStatement); WriteKeyword("fixed"); + Space(policy.UsingParentheses); LPar(); + Space(policy.WithinUsingParentheses); fixedStatement.Type.AcceptVisitor(this, data); + Space(); WriteCommaSeparatedList(fixedStatement.Variables); + Space(policy.WithinUsingParentheses); RPar(); WriteEmbeddedStatement(fixedStatement.EmbeddedStatement); return EndNode(fixedStatement);