diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index bdf2e36ca..ea56949f0 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -1804,6 +1804,10 @@ namespace ICSharpCode.Decompiler.CSharp { RemoveAttribute(entityDecl, KnownAttribute.DebuggerHidden); } + if (function.StateMachineCompiledWithLegacyVisualBasic) + { + RemoveAttribute(entityDecl, KnownAttribute.DebuggerStepThrough); + } } if (function.IsAsync) { diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs b/ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs index 4bd88cade..88c6f7915 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/SymbolicExecution.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team // // 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 @@ -118,7 +118,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow public SymbolicValue Eval(ILInstruction inst) { - if (inst is BinaryNumericInstruction bni && bni.Operator == BinaryNumericOperator.Sub && !bni.CheckForOverflow) + if (inst is BinaryNumericInstruction bni && bni.Operator == BinaryNumericOperator.Sub) { var left = Eval(bni.Left); var right = Eval(bni.Right); diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs index 099247f61..06206e596 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team // // 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 @@ -59,6 +59,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// Set in MatchEnumeratorCreationPattern() bool isCompiledWithMono; + /// Set in MatchEnumeratorCreationPattern() or ConstructExceptionTable() + bool isCompiledWithVisualBasic; + + /// Set in MatchEnumeratorCreationPattern() + /// If this is true, then isCompiledWithVisualBasic is also true. + bool isCompiledWithLegacyVisualBasic; + /// The dispose method of the compiler-generated enumerator class. /// Set in ConstructExceptionTable() MethodDefinitionHandle disposeMethod; @@ -100,6 +107,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// ILVariable skipFinallyBodies; + /// + /// Local bool variable in MoveNext() that signifies whether to execute finally bodies. + /// + ILVariable doFinallyBodies; + /// /// Set of variables might hold copies of the generated state field. /// @@ -115,6 +127,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow this.currentType = metadata.GetMethodDefinition((MethodDefinitionHandle)context.Function.Method.MetadataToken).GetDeclaringType(); this.enumeratorType = default; this.enumeratorCtor = default; + this.isCompiledWithMono = false; + this.isCompiledWithVisualBasic = false; + this.isCompiledWithLegacyVisualBasic = false; this.stateField = null; this.currentField = null; this.disposingField = null; @@ -123,6 +138,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow this.decompiledFinallyMethods.Clear(); this.returnStores.Clear(); this.skipFinallyBodies = null; + this.doFinallyBodies = null; this.cachedStateVars = null; if (!MatchEnumeratorCreationPattern(function)) return; @@ -144,6 +160,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow context.Step("Replacing body with MoveNext() body", function); function.IsIterator = true; function.StateMachineCompiledWithMono = isCompiledWithMono; + function.StateMachineCompiledWithLegacyVisualBasic = isCompiledWithLegacyVisualBasic; var oldBody = function.Body; function.Body = newBody; // register any locals used in newBody @@ -159,7 +176,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow context.Step("Delete unreachable blocks", function); - if (isCompiledWithMono) + if (isCompiledWithMono || isCompiledWithVisualBasic) { // mono has try-finally inline (like async on MS); we also need to sort nested blocks: foreach (var nestedContainer in newBody.Blocks.SelectMany(c => c.Descendants).OfType()) @@ -180,6 +197,14 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow { CleanSkipFinallyBodies(function); } + else if (isCompiledWithLegacyVisualBasic) + { + CleanDoFinallyBodies(function); + } + else if (isCompiledWithVisualBasic) + { + CleanFinallyStateChecks(function); + } else { DecompileFinallyBlocks(); @@ -192,6 +217,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow context.Step("Transform failed, roll it back", function); function.IsIterator = false; function.StateMachineCompiledWithMono = false; + function.StateMachineCompiledWithLegacyVisualBasic = false; function.Body = oldBody; function.Variables.RemoveDead(); function.Warnings.Add($"yield-return decompiler failed: {ex.Message}"); @@ -202,7 +228,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow TranslateFieldsToLocalAccess(function, function, fieldToParameterMap, isCompiledWithMono); // On mono, we still need to remove traces of the state variable(s): - if (isCompiledWithMono) + if (isCompiledWithMono || isCompiledWithVisualBasic) { if (fieldToParameterMap.TryGetValue(stateField, out var stateVar)) { @@ -275,18 +301,26 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow if (MatchEnumeratorCreationNewObj(newObj)) { pos++; // OK - isCompiledWithMono = false; } else if (MatchMonoEnumeratorCreationNewObj(newObj)) { pos++; - isCompiledWithMono = true; + if (TransformDisplayClassUsage.ValidateConstructor(context, ((NewObj)newObj).Method)) + { + isCompiledWithMono = true; + } + else + { + isCompiledWithVisualBasic = true; + isCompiledWithLegacyVisualBasic = true; + } } else { return false; } + bool stateFieldInitialized = false; for (; pos < body.Instructions.Count; pos++) { // stfld(..., ldloc(var_1), ldloc(parameter)) @@ -306,6 +340,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // copy of 'this' in struct fieldToParameterMap[(IField)storedField.MemberDefinition] = ((LdLoc)ldobj.Target).Variable; } + else if ((isCompiledWithMono || isCompiledWithLegacyVisualBasic) && (value.MatchLdcI4(-2) || value.MatchLdcI4(-1) || value.MatchLdcI4(0))) + { + stateField = (IField)storedField.MemberDefinition; + stateFieldInitialized = true; + } else { return false; @@ -319,7 +358,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // stloc(var_2, ldloc(var_1)) pos++; } - if (isCompiledWithMono) + if (isCompiledWithMono && !stateFieldInitialized) { // Mono initializes the state field separately: // (but not if it's left at the default value 0) @@ -328,7 +367,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow && (value.MatchLdcI4(-2) || value.MatchLdcI4(0))) { stateField = (IField)field.MemberDefinition; - isCompiledWithMono = true; pos++; } } @@ -477,8 +515,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow void AnalyzeCurrentProperty() { MethodDefinitionHandle getCurrentMethod = metadata.GetTypeDefinition(enumeratorType).GetMethods().FirstOrDefault( - m => metadata.GetString(metadata.GetMethodDefinition(m).Name).StartsWith("System.Collections.Generic.IEnumerator", StringComparison.Ordinal) - && metadata.GetString(metadata.GetMethodDefinition(m).Name).EndsWith(".get_Current", StringComparison.Ordinal)); + m => IsMethod(m, "get_Current")); Block body = SingleBlock(CreateILAst(getCurrentMethod, context).Body); if (body == null) throw new SymbolicAnalysisFailedException("get_Current has no body"); @@ -516,8 +553,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow void ResolveIEnumerableIEnumeratorFieldMapping() { MethodDefinitionHandle getEnumeratorMethod = metadata.GetTypeDefinition(enumeratorType).GetMethods().FirstOrDefault( - m => metadata.GetString(metadata.GetMethodDefinition(m).Name).StartsWith("System.Collections.Generic.IEnumerable", StringComparison.Ordinal) - && metadata.GetString(metadata.GetMethodDefinition(m).Name).EndsWith(".GetEnumerator", StringComparison.Ordinal)); + m => IsMethod(m, "GetEnumerator")); ResolveIEnumerableIEnumeratorFieldMapping(getEnumeratorMethod, context, fieldToParameterMap); } @@ -551,12 +587,26 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow void ConstructExceptionTable() { - if (isCompiledWithMono) + disposeMethod = metadata.GetTypeDefinition(enumeratorType).GetMethods().FirstOrDefault(m => IsMethod(m, "Dispose")); + var function = CreateILAst(disposeMethod, context); + + if (!isCompiledWithVisualBasic && !isCompiledWithMono) { - disposeMethod = metadata.GetTypeDefinition(enumeratorType).GetMethods().FirstOrDefault(m => metadata.GetString(metadata.GetMethodDefinition(m).Name) == "Dispose"); - var function = CreateILAst(disposeMethod, context); BlockContainer body = (BlockContainer)function.Body; + foreach (var instr in body.Blocks.SelectMany(block => block.Instructions)) + { + if (instr is CallInstruction call && call.Arguments.Count == 1 && call.Arguments[0].MatchLdThis() && + IsMethod((MethodDefinitionHandle)call.Method.MetadataToken, "MoveNext")) + { + isCompiledWithVisualBasic = true; + break; + } + } + } + if (isCompiledWithMono || isCompiledWithVisualBasic) + { + BlockContainer body = (BlockContainer)function.Body; for (var i = 0; (i < body.EntryPoint.Instructions.Count) && !(body.EntryPoint.Instructions[i] is Branch); i++) { if (body.EntryPoint.Instructions[i] is StObj stobj @@ -570,14 +620,12 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } } - // On mono, we don't need to analyse Dispose() to reconstruct the try-finally structure. + // On mono and VB, we don't need to analyse Dispose() to reconstruct the try-finally structure. finallyMethodToStateRange = default; } else { - // Non-Mono: analyze try-finally structure in Dispose() - disposeMethod = metadata.GetTypeDefinition(enumeratorType).GetMethods().FirstOrDefault(m => metadata.GetString(metadata.GetMethodDefinition(m).Name) == "System.IDisposable.Dispose"); - var function = CreateILAst(disposeMethod, context); + // Non-Mono/Non-VB: analyze try-finally structure in Dispose() var rangeAnalysis = new StateRangeAnalysis(StateRangeAnalysisMode.IteratorDispose, stateField); rangeAnalysis.AssignStateRanges(function.Body, LongSet.Universe); finallyMethodToStateRange = rangeAnalysis.finallyMethodToStateRange; @@ -616,6 +664,17 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow CopyPropagation.Propagate(stloc, context); } + // Copy propagate stack slots holding a 32 bit integer. + foreach (var stloc in moveNextFunction.Descendants.OfType().Where(s => s.Variable.Kind == VariableKind.StackSlot && s.Variable.IsSingleDefinition && s.Value.OpCode == OpCode.LdcI4).ToList()) + { + CopyPropagation.Propagate(stloc, context); + } + + foreach (var block in moveNextFunction.Descendants.OfType()) + { + block.Instructions.RemoveAll(inst => inst.OpCode == OpCode.LdcI4); + } + var body = (BlockContainer)moveNextFunction.Body; if (body.Blocks.Count == 1 && body.Blocks[0].Instructions.Count == 1 && body.Blocks[0].Instructions[0] is TryFault tryFault) { @@ -635,6 +694,35 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } } + if (isCompiledWithLegacyVisualBasic && (body.Blocks.Count == 2 || body.Blocks.Count == 1) && + body.Blocks[0].Instructions.Count == 2 && + body.Blocks[0].Instructions[0].MatchStLoc(out var firstVar, out var ldc) && ldc.MatchLdcI4(1)) + { + doFinallyBodies = firstVar; + if (body.Blocks[0].Instructions[1] is TryCatch tryCatch && tryCatch.Handlers.Count == 1) + { + TryCatchHandler catchHandler = tryCatch.Handlers[0]; + var catchBlockContainer = catchHandler.Body as BlockContainer; + if (catchBlockContainer?.Blocks.Count != 1) + throw new SymbolicAnalysisFailedException("Unexpected number of blocks in MoveNext() catch block"); + var catchBlock = catchBlockContainer.Blocks.Single(); + if (!(catchBlock.Instructions.Count == 4 && catchBlock.Instructions[0] is Call call && + call.Method.Name == "SetProjectError" && call.Arguments.Count == 1 && + call.Arguments[0].MatchLdLoc(catchHandler.Variable) && + catchBlock.Instructions[1].MatchStLoc(out _, out var ldloc) && + ldloc.MatchLdLoc(catchHandler.Variable) && + catchBlock.Instructions[2].MatchStFld(out var ldThis, out var fld, out var value) && + ldThis.MatchLdThis() && fld.MemberDefinition.Equals(stateField) && value is LdcI4 && + catchBlock.Instructions[3] is Rethrow)) + throw new SymbolicAnalysisFailedException("Unexpected catch block contents in MoveNext()"); + BlockContainer tryCatchBody = (BlockContainer)tryCatch.TryBlock; + // Move return block + if (body.Blocks.Count == 2) + tryCatchBody.Blocks.Add(body.Blocks[1]); + body = tryCatchBody; + } + } + if (stateField == null) { // With mono-compiled state machines, it's possible that we haven't discovered the state field @@ -690,6 +778,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow var rangeAnalysis = new StateRangeAnalysis(StateRangeAnalysisMode.IteratorMoveNext, stateField); rangeAnalysis.skipFinallyBodies = skipFinallyBodies; + rangeAnalysis.doFinallyBodies = doFinallyBodies; rangeAnalysis.CancellationToken = context.CancellationToken; rangeAnalysis.AssignStateRanges(body, LongSet.Universe); cachedStateVars = rangeAnalysis.CachedStateVars.ToHashSet(); @@ -813,7 +902,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // (this allows us to consider each block individually for try-finally reconstruction) newBlock = SplitBlock(newBlock, oldInst); } - else if (oldInst is TryFinally tryFinally && isCompiledWithMono) + else if (oldInst is TryFinally tryFinally && (isCompiledWithMono || isCompiledWithVisualBasic)) { // with mono, we have to recurse into try-finally blocks var oldTryBlock = (BlockContainer)tryFinally.TryBlock; @@ -821,6 +910,19 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow sra.AssignStateRanges(oldTryBlock, LongSet.Universe); tryFinally.TryBlock = ConvertBody(oldTryBlock, sra); } + else if (isCompiledWithLegacyVisualBasic && oldInst is IfInstruction ifInstruction && + ifInstruction.FalseInst.MatchNop() && + ifInstruction.Condition.MatchCompEquals(out var left, out var right) && + left.MatchLdFld(out var ldThis, out var fld) && ldThis.MatchLdThis() && + fld.MemberDefinition.Equals(disposingField) && + right.MatchLdcI4(0)) + { + newBlock.Instructions.Add(ifInstruction.TrueInst); + newBlock.AddILRange(ifInstruction.TrueInst); + UpdateBranchTargets(ifInstruction.TrueInst); + break; + } + // copy over the instruction to the new block newBlock.Instructions.Add(oldInst); newBlock.AddILRange(oldInst); @@ -828,12 +930,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } } - // Insert new artificial block as entry point, and jump to state 0. + // Insert new artificial block as entry point, and jump to the initial state. // This causes the method to start directly at the first user code, // and the whole compiler-generated state-dispatching logic becomes unreachable code // and gets deleted. + int initialState = isCompiledWithLegacyVisualBasic ? -1 : 0; newBody.Blocks.Insert(0, new Block { - Instructions = { MakeGoTo(0) } + Instructions = { MakeGoTo(initialState) } }); return newBody; @@ -870,10 +973,19 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } } - if (oldBlock.Instructions[pos].MatchStFld(out var target, out var field, out var value) + // Visual Basic Compiler emits additional stores to variables. + int? localNewState = null; + if (oldBlock.Instructions[pos].MatchStLoc(out _, out var value) && value is LdcI4 ldci4) + { + localNewState = ldci4.Value; + pos++; + } + + if (oldBlock.Instructions[pos].MatchStFld(out var target, out var field, out value) && target.MatchLdThis() && field.MemberDefinition == stateField - && value.MatchLdcI4(out int newState)) + && value.MatchLdcI4(out int newState) + && (localNewState is null || localNewState == newState)) { pos++; } @@ -899,6 +1011,19 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } pos++; } + // We can't use MatchStLoc like above since the doFinallyBodies variable is split by SplitVariables. + // This occurs for the Legacy VBC compiler. + if (oldBlock.Instructions[pos].MatchStLoc(out var var, out value) && var.Kind == VariableKind.Local && var.Index == doFinallyBodies.Index) + { + if (!value.MatchLdcI4(0)) + { + newBlock.Instructions.Add(new InvalidExpression { + ExpectedResultType = StackType.Void, + Message = "Unexpected assignment to doFinallyBodies" + }); + } + pos++; + } if (oldBlock.Instructions[pos].MatchReturn(out var retVal) && retVal.MatchLdcI4(1)) { @@ -1251,6 +1376,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } #endregion + #region Cleanup finally blocks /// /// Eliminates usage of doFinallyBodies @@ -1264,7 +1390,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow context.StepStartGroup("CleanFinallyBlocks", function); if (skipFinallyBodies.StoreInstructions.Count != 0 || skipFinallyBodies.AddressCount != 0) { - // misdetected another variable as doFinallyBodies? + // misdetected another variable as skipFinallyBodies? // Fortunately removing the initial store of 0 is harmless, as we // default-initialize the variable on uninit uses return; @@ -1318,5 +1444,96 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow return !call.Method.MetadataToken.IsNil; } } + + private void CleanDoFinallyBodies(ILFunction function) + { + if (doFinallyBodies == null) + { + return; // only VB code uses doFinallyBodies + } + context.StepStartGroup("CleanDoFinallyBodies", function); + if (doFinallyBodies.StoreInstructions.Count != 0 || doFinallyBodies.AddressCount != 0) + { + // misdetected another variable as skipFinallyBodies? + // Fortunately removing the initial store of 0 is harmless, as we + // default-initialize the variable on uninit uses + return; + } + foreach (var tryFinally in function.Descendants.OfType()) + { + if (!(tryFinally.FinallyBlock is BlockContainer container)) + continue; + Block entryPoint = AsyncAwaitDecompiler.GetBodyEntryPoint(container); + if (entryPoint?.Instructions[0] is IfInstruction ifInst) + { + if (ifInst.Condition.MatchCompEquals(out var left, out var right) && left.MatchLdLoc(doFinallyBodies) && right.MatchLdcI4(0)) + { + context.Step("Remove if (doFinallyBodies) from try-finally", tryFinally); + // condition will always be false now that we're using 'yield' instructions + entryPoint.Instructions.RemoveAt(0); + } + } + } + foreach (LdLoc load in doFinallyBodies.LoadInstructions.ToArray()) + { + load.ReplaceWith(new LdcI4(1).WithILRange(load)); + } + context.StepEndGroup(keepIfEmpty: true); + } + + private void CleanFinallyStateChecks(ILFunction function) + { + context.StepStartGroup("CleanFinallyStateChecks", function); + foreach (var tryFinally in function.Descendants.OfType()) + { + if (!(tryFinally.FinallyBlock is BlockContainer container)) + continue; + Block entryPoint = AsyncAwaitDecompiler.GetBodyEntryPoint(container); + if (entryPoint?.Instructions[0] is IfInstruction ifInst) + { + if (ifInst.Condition is Comp comp && comp.Kind == ComparisonKind.GreaterThanOrEqual && + comp.InputType == StackType.I4 && comp.Sign == Sign.Signed && comp.Left.MatchLdLoc(out var variable) && + cachedStateVars.Contains(variable) && + comp.Right.MatchLdcI4(0)) + { + context.Step("Remove if (stateVar >= 0) from try-finally", tryFinally); + // condition will always be false now that we're using 'yield' instructions + entryPoint.Instructions.RemoveAt(0); + } + } + } + context.StepEndGroup(keepIfEmpty: true); + } + + #endregion + + bool IsMethod(MethodDefinitionHandle method, string name) + { + var methodDefinition = metadata.GetMethodDefinition(method); + if (metadata.GetString(methodDefinition.Name) == name) + return true; + foreach (var implHandle in method.GetMethodImplementations(metadata)) + { + var impl = metadata.GetMethodImplementation(implHandle); + switch (impl.MethodDeclaration.Kind) + { + case HandleKind.MethodDefinition: + var md = metadata.GetMethodDefinition((MethodDefinitionHandle)impl.MethodDeclaration); + if (metadata.GetString(md.Name) != name) + continue; + return true; + case HandleKind.MemberReference: + var mr = metadata.GetMemberReference((MemberReferenceHandle)impl.MethodDeclaration); + if (mr.GetKind() != MemberReferenceKind.Method) + continue; + if (metadata.GetString(mr.Name) != name) + continue; + return true; + default: + continue; + } + } + return false; + } } } diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs index 306539276..bbf0e6441 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs @@ -1,4 +1,4 @@ -#nullable enable +#nullable enable // Copyright (c) 2014 Daniel Grunwald // // Permission is hereby granted, free of charge, to any person obtaining a copy of this @@ -98,6 +98,11 @@ namespace ICSharpCode.Decompiler.IL /// public bool StateMachineCompiledWithMono; + /// + /// Gets whether the YieldReturnDecompiler determined that the Legacy VB compiler was used to compile this function. + /// + public bool StateMachineCompiledWithLegacyVisualBasic; + /// /// Gets whether this function is async. /// This flag gets set by the AsyncAwaitDecompiler. diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index 5f69000d1..a62e16eb2 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2019 Siegfried Pammer +// Copyright (c) 2019 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 @@ -289,7 +289,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return null; if (!(v.StoreInstructions.SingleOrDefault() is StLoc stloc)) return null; - if (stloc.Value is NewObj newObj && ValidateConstructor(newObj.Method)) + if (stloc.Value is NewObj newObj && ValidateConstructor(context, newObj.Method)) { result = new DisplayClass(v, definition) { CaptureScope = v.CaptureScope, @@ -393,7 +393,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms var definition = newObj.Method.DeclaringType.GetDefinition(); if (!ValidateDisplayClassDefinition(definition)) return null; - if (!ValidateConstructor(newObj.Method)) + if (!ValidateConstructor(context, newObj.Method)) return null; if (!initializerBlock.Parent.MatchStLoc(out var referenceVariable)) return null; @@ -432,7 +432,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - private bool ValidateConstructor(IMethod method) + internal static bool ValidateConstructor(ILTransformContext context, IMethod method) { try { @@ -491,7 +491,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } - ILOpCode DecodeOpCodeSkipNop(ref BlobReader reader) + static ILOpCode DecodeOpCodeSkipNop(ref BlobReader reader) { ILOpCode code; do